xref: /petsc/src/dm/impls/plex/plex.c (revision 69eda9da2cab3444df2acd68fbbc2fdf1947414f)
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;
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: [](chapter_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(0);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(0);
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: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
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(0);
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   PetscCallMPI(MPI_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(0);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective on dm
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: [](chapter_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(0);
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(PetscStrcpy(tmpname, vname));
202         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
203         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
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(0);
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(0);
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 *coords, *array;
253   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
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(VecGetArrayRead(coordinates, &coords));
278   for (c = 0; c < N; c += dim) {
279     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
280     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
281     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
282     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
283   }
284   PetscCall(VecRestoreArrayRead(coordinates, &coords));
285   PetscCall(PetscDrawClear(draw));
286 
287   /* Could implement something like DMDASelectFields() */
288   for (f = 0; f < Nf; ++f) {
289     DM          fdm = dm;
290     Vec         fv  = v;
291     IS          fis;
292     char        prefix[PETSC_MAX_PATH_LEN];
293     const char *fname;
294 
295     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
296     PetscCall(PetscSectionGetFieldName(s, f, &fname));
297 
298     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
299     else prefix[0] = '\0';
300     if (Nf > 1) {
301       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
302       PetscCall(VecGetSubVector(v, fis, &fv));
303       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
304       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
305     }
306     for (comp = 0; comp < Nc; ++comp, ++w) {
307       PetscInt nmax = 2;
308 
309       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
310       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
311       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
312       PetscCall(PetscDrawSetTitle(draw, title));
313 
314       /* TODO Get max and min only for this component */
315       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
316       if (!flg) {
317         PetscCall(VecMin(fv, NULL, &vbound[0]));
318         PetscCall(VecMax(fv, NULL, &vbound[1]));
319         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
320       }
321       PetscCall(PetscDrawGetPopup(draw, &popup));
322       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
323       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
324 
325       PetscCall(VecGetArrayRead(fv, &array));
326       for (c = cStart; c < cEnd; ++c) {
327         PetscScalar *coords = NULL, *a   = NULL;
328         PetscInt     numCoords, color[4] = {-1, -1, -1, -1};
329 
330         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
331         if (a) {
332           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
333           color[1] = color[2] = color[3] = color[0];
334         } else {
335           PetscScalar *vals = NULL;
336           PetscInt     numVals, va;
337 
338           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
339           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);
340           switch (numVals / Nc) {
341           case 3: /* P1 Triangle */
342           case 4: /* P1 Quadrangle */
343             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
344             break;
345           case 6: /* P2 Triangle */
346           case 8: /* P2 Quadrangle */
347             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
348             break;
349           default:
350             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
351           }
352           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
353         }
354         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
355         switch (numCoords) {
356         case 6:
357         case 12: /* Localized triangle */
358           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]));
359           break;
360         case 8:
361         case 16: /* Localized quadrilateral */
362           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]));
363           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]));
364           break;
365         default:
366           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
367         }
368         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
369       }
370       PetscCall(VecRestoreArrayRead(fv, &array));
371       PetscCall(PetscDrawFlush(draw));
372       PetscCall(PetscDrawPause(draw));
373       PetscCall(PetscDrawSave(draw));
374     }
375     if (Nf > 1) {
376       PetscCall(VecRestoreSubVector(v, fis, &fv));
377       PetscCall(ISDestroy(&fis));
378       PetscCall(DMDestroy(&fdm));
379     }
380   }
381   PetscFunctionReturn(0);
382 }
383 
384 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
385 {
386   DM        dm;
387   PetscDraw draw;
388   PetscInt  dim;
389   PetscBool isnull;
390 
391   PetscFunctionBegin;
392   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
393   PetscCall(PetscDrawIsNull(draw, &isnull));
394   if (isnull) PetscFunctionReturn(0);
395 
396   PetscCall(VecGetDM(v, &dm));
397   PetscCall(DMGetCoordinateDim(dm, &dim));
398   switch (dim) {
399   case 1:
400     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
401     break;
402   case 2:
403     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
404     break;
405   default:
406     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
407   }
408   PetscFunctionReturn(0);
409 }
410 
411 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
412 {
413   DM                      dm;
414   Vec                     locv;
415   const char             *name;
416   PetscSection            section;
417   PetscInt                pStart, pEnd;
418   PetscInt                numFields;
419   PetscViewerVTKFieldType ft;
420 
421   PetscFunctionBegin;
422   PetscCall(VecGetDM(v, &dm));
423   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
424   PetscCall(PetscObjectGetName((PetscObject)v, &name));
425   PetscCall(PetscObjectSetName((PetscObject)locv, name));
426   PetscCall(VecCopy(v, locv));
427   PetscCall(DMGetLocalSection(dm, &section));
428   PetscCall(PetscSectionGetNumFields(section, &numFields));
429   if (!numFields) {
430     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
431     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
432   } else {
433     PetscInt f;
434 
435     for (f = 0; f < numFields; f++) {
436       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
437       if (ft == PETSC_VTK_INVALID) continue;
438       PetscCall(PetscObjectReference((PetscObject)locv));
439       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
440     }
441     PetscCall(VecDestroy(&locv));
442   }
443   PetscFunctionReturn(0);
444 }
445 
446 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
447 {
448   DM        dm;
449   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
450 
451   PetscFunctionBegin;
452   PetscCall(VecGetDM(v, &dm));
453   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
454   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
455   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
456   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
457   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
458   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
459   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
460     PetscInt    i, numFields;
461     PetscObject fe;
462     PetscBool   fem  = PETSC_FALSE;
463     Vec         locv = v;
464     const char *name;
465     PetscInt    step;
466     PetscReal   time;
467 
468     PetscCall(DMGetNumFields(dm, &numFields));
469     for (i = 0; i < numFields; i++) {
470       PetscCall(DMGetField(dm, i, NULL, &fe));
471       if (fe->classid == PETSCFE_CLASSID) {
472         fem = PETSC_TRUE;
473         break;
474       }
475     }
476     if (fem) {
477       PetscObject isZero;
478 
479       PetscCall(DMGetLocalVector(dm, &locv));
480       PetscCall(PetscObjectGetName((PetscObject)v, &name));
481       PetscCall(PetscObjectSetName((PetscObject)locv, name));
482       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
483       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
484       PetscCall(VecCopy(v, locv));
485       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
486       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
487     }
488     if (isvtk) {
489       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
490     } else if (ishdf5) {
491 #if defined(PETSC_HAVE_HDF5)
492       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
493 #else
494       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
495 #endif
496     } else if (isdraw) {
497       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
498     } else if (isglvis) {
499       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
500       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
501       PetscCall(VecView_GLVis(locv, viewer));
502     } else if (iscgns) {
503 #if defined(PETSC_HAVE_CGNS)
504       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
505 #else
506       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
507 #endif
508     }
509     if (fem) {
510       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
511       PetscCall(DMRestoreLocalVector(dm, &locv));
512     }
513   } else {
514     PetscBool isseq;
515 
516     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
517     if (isseq) PetscCall(VecView_Seq(v, viewer));
518     else PetscCall(VecView_MPI(v, viewer));
519   }
520   PetscFunctionReturn(0);
521 }
522 
523 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
524 {
525   DM        dm;
526   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
527 
528   PetscFunctionBegin;
529   PetscCall(VecGetDM(v, &dm));
530   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
532   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
533   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
534   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
535   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
536   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
537   if (isvtk || isdraw || isglvis || iscgns) {
538     Vec         locv;
539     PetscObject isZero;
540     const char *name;
541 
542     PetscCall(DMGetLocalVector(dm, &locv));
543     PetscCall(PetscObjectGetName((PetscObject)v, &name));
544     PetscCall(PetscObjectSetName((PetscObject)locv, name));
545     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
546     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
547     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
548     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
549     PetscCall(VecView_Plex_Local(locv, viewer));
550     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
551     PetscCall(DMRestoreLocalVector(dm, &locv));
552   } else if (ishdf5) {
553 #if defined(PETSC_HAVE_HDF5)
554     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
555 #else
556     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
557 #endif
558   } else if (isexodusii) {
559 #if defined(PETSC_HAVE_EXODUSII)
560     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
561 #else
562     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
563 #endif
564   } else {
565     PetscBool isseq;
566 
567     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
568     if (isseq) PetscCall(VecView_Seq(v, viewer));
569     else PetscCall(VecView_MPI(v, viewer));
570   }
571   PetscFunctionReturn(0);
572 }
573 
574 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
575 {
576   DM                dm;
577   MPI_Comm          comm;
578   PetscViewerFormat format;
579   Vec               v;
580   PetscBool         isvtk, ishdf5;
581 
582   PetscFunctionBegin;
583   PetscCall(VecGetDM(originalv, &dm));
584   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
585   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
586   PetscCall(PetscViewerGetFormat(viewer, &format));
587   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
588   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
589   if (format == PETSC_VIEWER_NATIVE) {
590     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
591     /* this need a better fix */
592     if (dm->useNatural) {
593       if (dm->sfNatural) {
594         const char *vecname;
595         PetscInt    n, nroots;
596 
597         PetscCall(VecGetLocalSize(originalv, &n));
598         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
599         if (n == nroots) {
600           PetscCall(DMGetGlobalVector(dm, &v));
601           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
602           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
603           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
604           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
605         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
606       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
607     } else v = originalv;
608   } else v = originalv;
609 
610   if (ishdf5) {
611 #if defined(PETSC_HAVE_HDF5)
612     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
613 #else
614     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
615 #endif
616   } else if (isvtk) {
617     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
618   } else {
619     PetscBool isseq;
620 
621     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
622     if (isseq) PetscCall(VecView_Seq(v, viewer));
623     else PetscCall(VecView_MPI(v, viewer));
624   }
625   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
626   PetscFunctionReturn(0);
627 }
628 
629 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
630 {
631   DM        dm;
632   PetscBool ishdf5;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
638   if (ishdf5) {
639     DM          dmBC;
640     Vec         gv;
641     const char *name;
642 
643     PetscCall(DMGetOutputDM(dm, &dmBC));
644     PetscCall(DMGetGlobalVector(dmBC, &gv));
645     PetscCall(PetscObjectGetName((PetscObject)v, &name));
646     PetscCall(PetscObjectSetName((PetscObject)gv, name));
647     PetscCall(VecLoad_Default(gv, viewer));
648     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
649     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
650     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
651   } else PetscCall(VecLoad_Default(v, viewer));
652   PetscFunctionReturn(0);
653 }
654 
655 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
656 {
657   DM        dm;
658   PetscBool ishdf5, isexodusii;
659 
660   PetscFunctionBegin;
661   PetscCall(VecGetDM(v, &dm));
662   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
663   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
664   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
665   if (ishdf5) {
666 #if defined(PETSC_HAVE_HDF5)
667     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
668 #else
669     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
670 #endif
671   } else if (isexodusii) {
672 #if defined(PETSC_HAVE_EXODUSII)
673     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
674 #else
675     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
676 #endif
677   } else PetscCall(VecLoad_Default(v, viewer));
678   PetscFunctionReturn(0);
679 }
680 
681 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
682 {
683   DM                dm;
684   PetscViewerFormat format;
685   PetscBool         ishdf5;
686 
687   PetscFunctionBegin;
688   PetscCall(VecGetDM(originalv, &dm));
689   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
690   PetscCall(PetscViewerGetFormat(viewer, &format));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
692   if (format == PETSC_VIEWER_NATIVE) {
693     if (dm->useNatural) {
694       if (dm->sfNatural) {
695         if (ishdf5) {
696 #if defined(PETSC_HAVE_HDF5)
697           Vec         v;
698           const char *vecname;
699 
700           PetscCall(DMGetGlobalVector(dm, &v));
701           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
702           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
703           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
704           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
705           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
706           PetscCall(DMRestoreGlobalVector(dm, &v));
707 #else
708           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
709 #endif
710         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
711       }
712     } else PetscCall(VecLoad_Default(originalv, viewer));
713   }
714   PetscFunctionReturn(0);
715 }
716 
717 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
718 {
719   PetscSection       coordSection;
720   Vec                coordinates;
721   DMLabel            depthLabel, celltypeLabel;
722   const char        *name[4];
723   const PetscScalar *a;
724   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
725 
726   PetscFunctionBegin;
727   PetscCall(DMGetDimension(dm, &dim));
728   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
729   PetscCall(DMGetCoordinateSection(dm, &coordSection));
730   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
731   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
732   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
733   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
734   PetscCall(VecGetArrayRead(coordinates, &a));
735   name[0]       = "vertex";
736   name[1]       = "edge";
737   name[dim - 1] = "face";
738   name[dim]     = "cell";
739   for (c = cStart; c < cEnd; ++c) {
740     PetscInt *closure = NULL;
741     PetscInt  closureSize, cl, ct;
742 
743     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
744     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
745     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
746     PetscCall(PetscViewerASCIIPushTab(viewer));
747     for (cl = 0; cl < closureSize * 2; cl += 2) {
748       PetscInt point = closure[cl], depth, dof, off, d, p;
749 
750       if ((point < pStart) || (point >= pEnd)) continue;
751       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
752       if (!dof) continue;
753       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
754       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
755       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
756       for (p = 0; p < dof / dim; ++p) {
757         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
758         for (d = 0; d < dim; ++d) {
759           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
760           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
761         }
762         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
763       }
764       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
765     }
766     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
767     PetscCall(PetscViewerASCIIPopTab(viewer));
768   }
769   PetscCall(VecRestoreArrayRead(coordinates, &a));
770   PetscFunctionReturn(0);
771 }
772 
773 typedef enum {
774   CS_CARTESIAN,
775   CS_POLAR,
776   CS_CYLINDRICAL,
777   CS_SPHERICAL
778 } CoordSystem;
779 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
780 
781 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
782 {
783   PetscInt i;
784 
785   PetscFunctionBegin;
786   if (dim > 3) {
787     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
788   } else {
789     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
790 
791     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
792     switch (cs) {
793     case CS_CARTESIAN:
794       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
795       break;
796     case CS_POLAR:
797       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       break;
801     case CS_CYLINDRICAL:
802       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
803       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
804       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
805       trcoords[2] = coords[2];
806       break;
807     case CS_SPHERICAL:
808       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
809       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
810       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
811       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
812       break;
813     }
814     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
815   }
816   PetscFunctionReturn(0);
817 }
818 
819 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
820 {
821   DM_Plex          *mesh = (DM_Plex *)dm->data;
822   DM                cdm, cdmCell;
823   PetscSection      coordSection, coordSectionCell;
824   Vec               coordinates, coordinatesCell;
825   PetscViewerFormat format;
826 
827   PetscFunctionBegin;
828   PetscCall(PetscViewerGetFormat(viewer, &format));
829   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
830     const char *name;
831     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
832     PetscInt    pStart, pEnd, p, numLabels, l;
833     PetscMPIInt rank, size;
834 
835     PetscCall(DMGetCoordinateDM(dm, &cdm));
836     PetscCall(DMGetCoordinateSection(dm, &coordSection));
837     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
838     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
839     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
840     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
841     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
842     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
843     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
844     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
845     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
846     PetscCall(DMGetDimension(dm, &dim));
847     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
848     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
849     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
850     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
851     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
852     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
853     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
854     for (p = pStart; p < pEnd; ++p) {
855       PetscInt dof, off, s;
856 
857       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
858       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
859       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
860     }
861     PetscCall(PetscViewerFlush(viewer));
862     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
863     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
864     for (p = pStart; p < pEnd; ++p) {
865       PetscInt dof, off, c;
866 
867       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
868       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
869       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]));
870     }
871     PetscCall(PetscViewerFlush(viewer));
872     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
873     if (coordSection && coordinates) {
874       CoordSystem        cs = CS_CARTESIAN;
875       const PetscScalar *array, *arrayCell = NULL;
876       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
877       PetscMPIInt        rank;
878       const char        *name;
879 
880       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
881       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
882       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
883       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
884       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
885       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
886       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
887       pStart = PetscMin(pvStart, pcStart);
888       pEnd   = PetscMax(pvEnd, pcEnd);
889       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
890       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
891       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
892       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
893 
894       PetscCall(VecGetArrayRead(coordinates, &array));
895       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
896       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
897       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
898       for (p = pStart; p < pEnd; ++p) {
899         PetscInt dof, off;
900 
901         if (p >= pvStart && p < pvEnd) {
902           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
903           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
904           if (dof) {
905             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
906             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
907             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
908           }
909         }
910         if (cdmCell && p >= pcStart && p < pcEnd) {
911           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
912           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
913           if (dof) {
914             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
915             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
916             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
917           }
918         }
919       }
920       PetscCall(PetscViewerFlush(viewer));
921       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
922       PetscCall(VecRestoreArrayRead(coordinates, &array));
923       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
924     }
925     PetscCall(DMGetNumLabels(dm, &numLabels));
926     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
927     for (l = 0; l < numLabels; ++l) {
928       DMLabel     label;
929       PetscBool   isdepth;
930       const char *name;
931 
932       PetscCall(DMGetLabelName(dm, l, &name));
933       PetscCall(PetscStrcmp(name, "depth", &isdepth));
934       if (isdepth) continue;
935       PetscCall(DMGetLabel(dm, name, &label));
936       PetscCall(DMLabelView(label, viewer));
937     }
938     if (size > 1) {
939       PetscSF sf;
940 
941       PetscCall(DMGetPointSF(dm, &sf));
942       PetscCall(PetscSFView(sf, viewer));
943     }
944     PetscCall(PetscViewerFlush(viewer));
945   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
946     const char  *name, *color;
947     const char  *defcolors[3]  = {"gray", "orange", "green"};
948     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
949     char         lname[PETSC_MAX_PATH_LEN];
950     PetscReal    scale      = 2.0;
951     PetscReal    tikzscale  = 1.0;
952     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
953     double       tcoords[3];
954     PetscScalar *coords;
955     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
956     PetscMPIInt  rank, size;
957     char       **names, **colors, **lcolors;
958     PetscBool    flg, lflg;
959     PetscBT      wp = NULL;
960     PetscInt     pEnd, pStart;
961 
962     PetscCall(DMGetCoordinateDM(dm, &cdm));
963     PetscCall(DMGetCoordinateSection(dm, &coordSection));
964     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
965     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
966     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
967     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
968     PetscCall(DMGetDimension(dm, &dim));
969     PetscCall(DMPlexGetDepth(dm, &depth));
970     PetscCall(DMGetNumLabels(dm, &numLabels));
971     numLabels  = PetscMax(numLabels, 10);
972     numColors  = 10;
973     numLColors = 10;
974     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
975     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
976     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
977     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
978     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
979     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
980     n = 4;
981     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
982     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
983     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
984     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
985     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
986     if (!useLabels) numLabels = 0;
987     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
988     if (!useColors) {
989       numColors = 3;
990       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
991     }
992     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
993     if (!useColors) {
994       numLColors = 4;
995       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
996     }
997     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
998     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
999     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1000     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1001     if (depth < dim) plotEdges = PETSC_FALSE;
1002     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1003 
1004     /* filter points with labelvalue != labeldefaultvalue */
1005     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1006     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1007     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1008     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1009     if (lflg) {
1010       DMLabel lbl;
1011 
1012       PetscCall(DMGetLabel(dm, lname, &lbl));
1013       if (lbl) {
1014         PetscInt val, defval;
1015 
1016         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1017         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1018         for (c = pStart; c < pEnd; c++) {
1019           PetscInt *closure = NULL;
1020           PetscInt  closureSize;
1021 
1022           PetscCall(DMLabelGetValue(lbl, c, &val));
1023           if (val == defval) continue;
1024 
1025           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1026           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1027           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1028         }
1029       }
1030     }
1031 
1032     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1033     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1034     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1035     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1036 \\documentclass[tikz]{standalone}\n\n\
1037 \\usepackage{pgflibraryshapes}\n\
1038 \\usetikzlibrary{backgrounds}\n\
1039 \\usetikzlibrary{arrows}\n\
1040 \\begin{document}\n"));
1041     if (size > 1) {
1042       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1043       for (p = 0; p < size; ++p) {
1044         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1045         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1046       }
1047       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1048     }
1049     if (drawHasse) {
1050       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1051 
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1060       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1061       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1062       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1063       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1064     }
1065     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1066 
1067     /* Plot vertices */
1068     PetscCall(VecGetArray(coordinates, &coords));
1069     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1070     for (v = vStart; v < vEnd; ++v) {
1071       PetscInt  off, dof, d;
1072       PetscBool isLabeled = PETSC_FALSE;
1073 
1074       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1075       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1076       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1077       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1078       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1079       for (d = 0; d < dof; ++d) {
1080         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1081         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1082       }
1083       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1084       if (dim == 3) {
1085         PetscReal tmp = tcoords[1];
1086         tcoords[1]    = tcoords[2];
1087         tcoords[2]    = -tmp;
1088       }
1089       for (d = 0; d < dof; ++d) {
1090         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1091         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1092       }
1093       if (drawHasse) color = colors[0 % numColors];
1094       else color = colors[rank % numColors];
1095       for (l = 0; l < numLabels; ++l) {
1096         PetscInt val;
1097         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1098         if (val >= 0) {
1099           color     = lcolors[l % numLColors];
1100           isLabeled = PETSC_TRUE;
1101           break;
1102         }
1103       }
1104       if (drawNumbers[0]) {
1105         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1106       } else if (drawColors[0]) {
1107         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1108       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1109     }
1110     PetscCall(VecRestoreArray(coordinates, &coords));
1111     PetscCall(PetscViewerFlush(viewer));
1112     /* Plot edges */
1113     if (plotEdges) {
1114       PetscCall(VecGetArray(coordinates, &coords));
1115       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1116       for (e = eStart; e < eEnd; ++e) {
1117         const PetscInt *cone;
1118         PetscInt        coneSize, offA, offB, dof, d;
1119 
1120         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1121         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1122         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1123         PetscCall(DMPlexGetCone(dm, e, &cone));
1124         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1125         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1126         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1127         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1128         for (d = 0; d < dof; ++d) {
1129           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1130           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1131         }
1132         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1133         if (dim == 3) {
1134           PetscReal tmp = tcoords[1];
1135           tcoords[1]    = tcoords[2];
1136           tcoords[2]    = -tmp;
1137         }
1138         for (d = 0; d < dof; ++d) {
1139           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1140           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1141         }
1142         if (drawHasse) color = colors[1 % numColors];
1143         else color = colors[rank % numColors];
1144         for (l = 0; l < numLabels; ++l) {
1145           PetscInt val;
1146           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1147           if (val >= 0) {
1148             color = lcolors[l % numLColors];
1149             break;
1150           }
1151         }
1152         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1153       }
1154       PetscCall(VecRestoreArray(coordinates, &coords));
1155       PetscCall(PetscViewerFlush(viewer));
1156       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1157     }
1158     /* Plot cells */
1159     if (dim == 3 || !drawNumbers[1]) {
1160       for (e = eStart; e < eEnd; ++e) {
1161         const PetscInt *cone;
1162 
1163         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1164         color = colors[rank % numColors];
1165         for (l = 0; l < numLabels; ++l) {
1166           PetscInt val;
1167           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1168           if (val >= 0) {
1169             color = lcolors[l % numLColors];
1170             break;
1171           }
1172         }
1173         PetscCall(DMPlexGetCone(dm, e, &cone));
1174         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1175       }
1176     } else {
1177       DMPolytopeType ct;
1178 
1179       /* Drawing a 2D polygon */
1180       for (c = cStart; c < cEnd; ++c) {
1181         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1182         PetscCall(DMPlexGetCellType(dm, c, &ct));
1183         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1184           const PetscInt *cone;
1185           PetscInt        coneSize, e;
1186 
1187           PetscCall(DMPlexGetCone(dm, c, &cone));
1188           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1189           for (e = 0; e < coneSize; ++e) {
1190             const PetscInt *econe;
1191 
1192             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1193             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));
1194           }
1195         } else {
1196           PetscInt *closure = NULL;
1197           PetscInt  closureSize, Nv = 0, v;
1198 
1199           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1200           for (p = 0; p < closureSize * 2; p += 2) {
1201             const PetscInt point = closure[p];
1202 
1203             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1204           }
1205           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1206           for (v = 0; v <= Nv; ++v) {
1207             const PetscInt vertex = closure[v % Nv];
1208 
1209             if (v > 0) {
1210               if (plotEdges) {
1211                 const PetscInt *edge;
1212                 PetscInt        endpoints[2], ne;
1213 
1214                 endpoints[0] = closure[v - 1];
1215                 endpoints[1] = vertex;
1216                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1217                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1218                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1219                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1220               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1221             }
1222             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1223           }
1224           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1225           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1226         }
1227       }
1228     }
1229     for (c = cStart; c < cEnd; ++c) {
1230       double             ccoords[3] = {0.0, 0.0, 0.0};
1231       PetscBool          isLabeled  = PETSC_FALSE;
1232       PetscScalar       *cellCoords = NULL;
1233       const PetscScalar *array;
1234       PetscInt           numCoords, cdim, d;
1235       PetscBool          isDG;
1236 
1237       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1238       PetscCall(DMGetCoordinateDim(dm, &cdim));
1239       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1240       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1241       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1242       for (p = 0; p < numCoords / cdim; ++p) {
1243         for (d = 0; d < cdim; ++d) {
1244           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1245           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1246         }
1247         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1248         if (cdim == 3) {
1249           PetscReal tmp = tcoords[1];
1250           tcoords[1]    = tcoords[2];
1251           tcoords[2]    = -tmp;
1252         }
1253         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1254       }
1255       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1256       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1257       for (d = 0; d < cdim; ++d) {
1258         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1259         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1260       }
1261       if (drawHasse) color = colors[depth % numColors];
1262       else color = colors[rank % numColors];
1263       for (l = 0; l < numLabels; ++l) {
1264         PetscInt val;
1265         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1266         if (val >= 0) {
1267           color     = lcolors[l % numLColors];
1268           isLabeled = PETSC_TRUE;
1269           break;
1270         }
1271       }
1272       if (drawNumbers[dim]) {
1273         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1274       } else if (drawColors[dim]) {
1275         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1276       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1277     }
1278     if (drawHasse) {
1279       color = colors[depth % numColors];
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1281       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1282       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1285 
1286       color = colors[1 % numColors];
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1288       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1289       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1292 
1293       color = colors[0 % numColors];
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1295       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1296       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1297       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1298       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1299 
1300       for (p = pStart; p < pEnd; ++p) {
1301         const PetscInt *cone;
1302         PetscInt        coneSize, cp;
1303 
1304         PetscCall(DMPlexGetCone(dm, p, &cone));
1305         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1306         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1307       }
1308     }
1309     PetscCall(PetscViewerFlush(viewer));
1310     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1311     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1312     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1313     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1314     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1315     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1316     PetscCall(PetscFree3(names, colors, lcolors));
1317     PetscCall(PetscBTDestroy(&wp));
1318   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1319     Vec                    cown, acown;
1320     VecScatter             sct;
1321     ISLocalToGlobalMapping g2l;
1322     IS                     gid, acis;
1323     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1324     MPI_Group              ggroup, ngroup;
1325     PetscScalar           *array, nid;
1326     const PetscInt        *idxs;
1327     PetscInt              *idxs2, *start, *adjacency, *work;
1328     PetscInt64             lm[3], gm[3];
1329     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1330     PetscMPIInt            d1, d2, rank;
1331 
1332     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1333     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1334 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1335     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1336 #endif
1337     if (ncomm != MPI_COMM_NULL) {
1338       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1339       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1340       d1 = 0;
1341       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1342       nid = d2;
1343       PetscCallMPI(MPI_Group_free(&ggroup));
1344       PetscCallMPI(MPI_Group_free(&ngroup));
1345       PetscCallMPI(MPI_Comm_free(&ncomm));
1346     } else nid = 0.0;
1347 
1348     /* Get connectivity */
1349     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1350     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1351 
1352     /* filter overlapped local cells */
1353     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1354     PetscCall(ISGetIndices(gid, &idxs));
1355     PetscCall(ISGetLocalSize(gid, &cum));
1356     PetscCall(PetscMalloc1(cum, &idxs2));
1357     for (c = cStart, cum = 0; c < cEnd; c++) {
1358       if (idxs[c - cStart] < 0) continue;
1359       idxs2[cum++] = idxs[c - cStart];
1360     }
1361     PetscCall(ISRestoreIndices(gid, &idxs));
1362     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1363     PetscCall(ISDestroy(&gid));
1364     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1365 
1366     /* support for node-aware cell locality */
1367     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1368     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1369     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1370     PetscCall(VecGetArray(cown, &array));
1371     for (c = 0; c < numVertices; c++) array[c] = nid;
1372     PetscCall(VecRestoreArray(cown, &array));
1373     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1374     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1375     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1376     PetscCall(ISDestroy(&acis));
1377     PetscCall(VecScatterDestroy(&sct));
1378     PetscCall(VecDestroy(&cown));
1379 
1380     /* compute edgeCut */
1381     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1382     PetscCall(PetscMalloc1(cum, &work));
1383     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1384     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1385     PetscCall(ISDestroy(&gid));
1386     PetscCall(VecGetArray(acown, &array));
1387     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1388       PetscInt totl;
1389 
1390       totl = start[c + 1] - start[c];
1391       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1392       for (i = 0; i < totl; i++) {
1393         if (work[i] < 0) {
1394           ect += 1;
1395           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1396         }
1397       }
1398     }
1399     PetscCall(PetscFree(work));
1400     PetscCall(VecRestoreArray(acown, &array));
1401     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1402     lm[1] = -numVertices;
1403     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1404     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1405     lm[0] = ect;                     /* edgeCut */
1406     lm[1] = ectn;                    /* node-aware edgeCut */
1407     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1408     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1409     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1410 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1411     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1412 #else
1413     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1414 #endif
1415     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1416     PetscCall(PetscFree(start));
1417     PetscCall(PetscFree(adjacency));
1418     PetscCall(VecDestroy(&acown));
1419   } else {
1420     const char    *name;
1421     PetscInt      *sizes, *hybsizes, *ghostsizes;
1422     PetscInt       locDepth, depth, cellHeight, dim, d;
1423     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1424     PetscInt       numLabels, l, maxSize = 17;
1425     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1426     MPI_Comm       comm;
1427     PetscMPIInt    size, rank;
1428 
1429     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1430     PetscCallMPI(MPI_Comm_size(comm, &size));
1431     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1432     PetscCall(DMGetDimension(dm, &dim));
1433     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1434     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1435     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1436     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1437     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1438     PetscCall(DMPlexGetDepth(dm, &locDepth));
1439     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1440     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1441     gcNum = gcEnd - gcStart;
1442     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1443     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1444     for (d = 0; d <= depth; d++) {
1445       PetscInt Nc[2] = {0, 0}, ict;
1446 
1447       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1448       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1449       ict = ct0;
1450       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1451       ct0 = (DMPolytopeType)ict;
1452       for (p = pStart; p < pEnd; ++p) {
1453         DMPolytopeType ct;
1454 
1455         PetscCall(DMPlexGetCellType(dm, p, &ct));
1456         if (ct == ct0) ++Nc[0];
1457         else ++Nc[1];
1458       }
1459       if (size < maxSize) {
1460         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1461         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1462         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1463         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1464         for (p = 0; p < size; ++p) {
1465           if (rank == 0) {
1466             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1467             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1468             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1469           }
1470         }
1471       } else {
1472         PetscInt locMinMax[2];
1473 
1474         locMinMax[0] = Nc[0] + Nc[1];
1475         locMinMax[1] = Nc[0] + Nc[1];
1476         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1477         locMinMax[0] = Nc[1];
1478         locMinMax[1] = Nc[1];
1479         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1480         if (d == depth) {
1481           locMinMax[0] = gcNum;
1482           locMinMax[1] = gcNum;
1483           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1484         }
1485         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1486         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1487         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1488         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1489       }
1490       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1491     }
1492     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1493     {
1494       const PetscReal *maxCell;
1495       const PetscReal *L;
1496       PetscBool        localized;
1497 
1498       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1499       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1500       if (L || localized) {
1501         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1502         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1503         if (L) {
1504           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1505           for (d = 0; d < dim; ++d) {
1506             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1507             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1508           }
1509           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1510         }
1511         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1512         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1513       }
1514     }
1515     PetscCall(DMGetNumLabels(dm, &numLabels));
1516     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1517     for (l = 0; l < numLabels; ++l) {
1518       DMLabel         label;
1519       const char     *name;
1520       IS              valueIS;
1521       const PetscInt *values;
1522       PetscInt        numValues, v;
1523 
1524       PetscCall(DMGetLabelName(dm, l, &name));
1525       PetscCall(DMGetLabel(dm, name, &label));
1526       PetscCall(DMLabelGetNumValues(label, &numValues));
1527       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1528       PetscCall(DMLabelGetValueIS(label, &valueIS));
1529       PetscCall(ISGetIndices(valueIS, &values));
1530       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1531       for (v = 0; v < numValues; ++v) {
1532         PetscInt size;
1533 
1534         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1535         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1536         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1537       }
1538       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1539       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1540       PetscCall(ISRestoreIndices(valueIS, &values));
1541       PetscCall(ISDestroy(&valueIS));
1542     }
1543     {
1544       char    **labelNames;
1545       PetscInt  Nl = numLabels;
1546       PetscBool flg;
1547 
1548       PetscCall(PetscMalloc1(Nl, &labelNames));
1549       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1550       for (l = 0; l < Nl; ++l) {
1551         DMLabel label;
1552 
1553         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1554         if (flg) {
1555           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1556           PetscCall(DMLabelView(label, viewer));
1557         }
1558         PetscCall(PetscFree(labelNames[l]));
1559       }
1560       PetscCall(PetscFree(labelNames));
1561     }
1562     /* If no fields are specified, people do not want to see adjacency */
1563     if (dm->Nf) {
1564       PetscInt f;
1565 
1566       for (f = 0; f < dm->Nf; ++f) {
1567         const char *name;
1568 
1569         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1570         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1571         PetscCall(PetscViewerASCIIPushTab(viewer));
1572         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1573         if (dm->fields[f].adjacency[0]) {
1574           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1575           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1576         } else {
1577           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1578           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1579         }
1580         PetscCall(PetscViewerASCIIPopTab(viewer));
1581       }
1582     }
1583     PetscCall(DMGetCoarseDM(dm, &cdm));
1584     if (cdm) {
1585       PetscCall(PetscViewerASCIIPushTab(viewer));
1586       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1587       PetscCall(DMPlexView_Ascii(cdm, viewer));
1588       PetscCall(PetscViewerASCIIPopTab(viewer));
1589     }
1590   }
1591   PetscFunctionReturn(0);
1592 }
1593 
1594 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1595 {
1596   DMPolytopeType ct;
1597   PetscMPIInt    rank;
1598   PetscInt       cdim;
1599 
1600   PetscFunctionBegin;
1601   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1602   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1603   PetscCall(DMGetCoordinateDim(dm, &cdim));
1604   switch (ct) {
1605   case DM_POLYTOPE_SEGMENT:
1606   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1607     switch (cdim) {
1608     case 1: {
1609       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1610       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1611 
1612       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1613       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1614       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1615     } break;
1616     case 2: {
1617       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1618       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1619       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1620 
1621       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1622       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));
1623       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));
1624     } break;
1625     default:
1626       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1627     }
1628     break;
1629   case DM_POLYTOPE_TRIANGLE:
1630     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));
1631     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1632     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1633     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1634     break;
1635   case DM_POLYTOPE_QUADRILATERAL:
1636     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));
1637     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));
1638     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1639     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1640     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1641     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1642     break;
1643   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1644     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));
1645     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));
1646     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1647     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1648     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1649     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1650     break;
1651   case DM_POLYTOPE_FV_GHOST:
1652     break;
1653   default:
1654     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1655   }
1656   PetscFunctionReturn(0);
1657 }
1658 
1659 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1660 {
1661   DMPolytopeType ct;
1662   PetscReal      centroid[2] = {0., 0.};
1663   PetscMPIInt    rank;
1664   PetscInt       fillColor, v, e, d;
1665 
1666   PetscFunctionBegin;
1667   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1668   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1669   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1670   switch (ct) {
1671   case DM_POLYTOPE_TRIANGLE: {
1672     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1673 
1674     for (v = 0; v < 3; ++v) {
1675       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1676       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1677     }
1678     for (e = 0; e < 3; ++e) {
1679       refCoords[0] = refVertices[e * 2 + 0];
1680       refCoords[1] = refVertices[e * 2 + 1];
1681       for (d = 1; d <= edgeDiv; ++d) {
1682         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1683         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1684       }
1685       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1686       for (d = 0; d < edgeDiv; ++d) {
1687         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));
1688         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1689       }
1690     }
1691   } break;
1692   default:
1693     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1694   }
1695   PetscFunctionReturn(0);
1696 }
1697 
1698 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1699 {
1700   PetscDraw          draw;
1701   DM                 cdm;
1702   PetscSection       coordSection;
1703   Vec                coordinates;
1704   const PetscScalar *coords;
1705   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1706   PetscReal         *refCoords, *edgeCoords;
1707   PetscBool          isnull, drawAffine = PETSC_TRUE;
1708   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1709 
1710   PetscFunctionBegin;
1711   PetscCall(DMGetCoordinateDim(dm, &dim));
1712   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1713   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1714   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1715   PetscCall(DMGetCoordinateDM(dm, &cdm));
1716   PetscCall(DMGetLocalSection(cdm, &coordSection));
1717   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1718   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1719   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1720 
1721   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1722   PetscCall(PetscDrawIsNull(draw, &isnull));
1723   if (isnull) PetscFunctionReturn(0);
1724   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1725 
1726   PetscCall(VecGetLocalSize(coordinates, &N));
1727   PetscCall(VecGetArrayRead(coordinates, &coords));
1728   for (c = 0; c < N; c += dim) {
1729     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1730     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1731     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1732     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1733   }
1734   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1735   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1736   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1737   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1738   PetscCall(PetscDrawClear(draw));
1739 
1740   for (c = cStart; c < cEnd; ++c) {
1741     PetscScalar *coords = NULL;
1742     PetscInt     numCoords;
1743 
1744     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1745     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1746     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1747     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1748   }
1749   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1750   PetscCall(PetscDrawFlush(draw));
1751   PetscCall(PetscDrawPause(draw));
1752   PetscCall(PetscDrawSave(draw));
1753   PetscFunctionReturn(0);
1754 }
1755 
1756 #if defined(PETSC_HAVE_EXODUSII)
1757   #include <exodusII.h>
1758   #include <petscviewerexodusii.h>
1759 #endif
1760 
1761 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1762 {
1763   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1764   char      name[PETSC_MAX_PATH_LEN];
1765 
1766   PetscFunctionBegin;
1767   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1768   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1769   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1770   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1771   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1772   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1773   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1774   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1775   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1776   if (iascii) {
1777     PetscViewerFormat format;
1778     PetscCall(PetscViewerGetFormat(viewer, &format));
1779     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1780     else PetscCall(DMPlexView_Ascii(dm, viewer));
1781   } else if (ishdf5) {
1782 #if defined(PETSC_HAVE_HDF5)
1783     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1784 #else
1785     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1786 #endif
1787   } else if (isvtk) {
1788     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1789   } else if (isdraw) {
1790     PetscCall(DMPlexView_Draw(dm, viewer));
1791   } else if (isglvis) {
1792     PetscCall(DMPlexView_GLVis(dm, viewer));
1793 #if defined(PETSC_HAVE_EXODUSII)
1794   } else if (isexodus) {
1795     /*
1796       exodusII requires that all sets be part of exactly one cell set.
1797       If the dm does not have a "Cell Sets" label defined, we create one
1798       with ID 1, containig all cells.
1799       Note that if the Cell Sets label is defined but does not cover all cells,
1800       we may still have a problem. This should probably be checked here or in the viewer;
1801     */
1802     PetscInt numCS;
1803     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1804     if (!numCS) {
1805       PetscInt cStart, cEnd, c;
1806       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1807       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1808       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1809     }
1810     PetscCall(DMView_PlexExodusII(dm, viewer));
1811 #endif
1812 #if defined(PETSC_HAVE_CGNS)
1813   } else if (iscgns) {
1814     PetscCall(DMView_PlexCGNS(dm, viewer));
1815 #endif
1816   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1817   /* Optionally view the partition */
1818   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1819   if (flg) {
1820     Vec ranks;
1821     PetscCall(DMPlexCreateRankField(dm, &ranks));
1822     PetscCall(VecView(ranks, viewer));
1823     PetscCall(VecDestroy(&ranks));
1824   }
1825   /* Optionally view a label */
1826   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1827   if (flg) {
1828     DMLabel label;
1829     Vec     val;
1830 
1831     PetscCall(DMGetLabel(dm, name, &label));
1832     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1833     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1834     PetscCall(VecView(val, viewer));
1835     PetscCall(VecDestroy(&val));
1836   }
1837   PetscFunctionReturn(0);
1838 }
1839 
1840 /*@
1841   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1842 
1843   Collective on dm
1844 
1845   Input Parameters:
1846 + dm     - The `DM` whose topology is to be saved
1847 - viewer - The `PetscViewer` to save it in
1848 
1849   Level: advanced
1850 
1851 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1852 @*/
1853 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1854 {
1855   PetscBool ishdf5;
1856 
1857   PetscFunctionBegin;
1858   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1859   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1860   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1861   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1862   if (ishdf5) {
1863 #if defined(PETSC_HAVE_HDF5)
1864     PetscViewerFormat format;
1865     PetscCall(PetscViewerGetFormat(viewer, &format));
1866     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1867       IS globalPointNumbering;
1868 
1869       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1870       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1871       PetscCall(ISDestroy(&globalPointNumbering));
1872     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1873 #else
1874     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1875 #endif
1876   }
1877   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1878   PetscFunctionReturn(0);
1879 }
1880 
1881 /*@
1882   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1883 
1884   Collective on dm
1885 
1886   Input Parameters:
1887 + dm     - The `DM` whose coordinates are to be saved
1888 - viewer - The `PetscViewer` for saving
1889 
1890   Level: advanced
1891 
1892 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1893 @*/
1894 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1895 {
1896   PetscBool ishdf5;
1897 
1898   PetscFunctionBegin;
1899   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1900   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1901   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1902   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1903   if (ishdf5) {
1904 #if defined(PETSC_HAVE_HDF5)
1905     PetscViewerFormat format;
1906     PetscCall(PetscViewerGetFormat(viewer, &format));
1907     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1908       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1909     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1910 #else
1911     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1912 #endif
1913   }
1914   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1915   PetscFunctionReturn(0);
1916 }
1917 
1918 /*@
1919   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1920 
1921   Collective on dm
1922 
1923   Input Parameters:
1924 + dm     - The `DM` whose labels are to be saved
1925 - viewer - The `PetscViewer` for saving
1926 
1927   Level: advanced
1928 
1929 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1930 @*/
1931 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1932 {
1933   PetscBool ishdf5;
1934 
1935   PetscFunctionBegin;
1936   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1937   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1938   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1939   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1940   if (ishdf5) {
1941 #if defined(PETSC_HAVE_HDF5)
1942     IS                globalPointNumbering;
1943     PetscViewerFormat format;
1944 
1945     PetscCall(PetscViewerGetFormat(viewer, &format));
1946     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1947       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1948       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1949       PetscCall(ISDestroy(&globalPointNumbering));
1950     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1951 #else
1952     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1953 #endif
1954   }
1955   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1956   PetscFunctionReturn(0);
1957 }
1958 
1959 /*@
1960   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1961 
1962   Collective on dm
1963 
1964   Input Parameters:
1965 + dm         - The `DM` that contains the topology on which the section to be saved is defined
1966 . viewer     - The `PetscViewer` for saving
1967 - sectiondm  - The `DM` that contains the section to be saved
1968 
1969   Level: advanced
1970 
1971   Notes:
1972   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.
1973 
1974   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.
1975 
1976 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1977 @*/
1978 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1979 {
1980   PetscBool ishdf5;
1981 
1982   PetscFunctionBegin;
1983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1984   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1985   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1986   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1987   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1988   if (ishdf5) {
1989 #if defined(PETSC_HAVE_HDF5)
1990     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1991 #else
1992     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1993 #endif
1994   }
1995   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1996   PetscFunctionReturn(0);
1997 }
1998 
1999 /*@
2000   DMPlexGlobalVectorView - Saves a global vector
2001 
2002   Collective on dm
2003 
2004   Input Parameters:
2005 + dm        - The `DM` that represents the topology
2006 . viewer    - The `PetscViewer` to save data with
2007 . sectiondm - The `DM` that contains the global section on which vec is defined
2008 - vec       - The global vector to be saved
2009 
2010   Level: advanced
2011 
2012   Notes:
2013   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.
2014 
2015   Typical calling sequence:
2016 .vb
2017        DMCreate(PETSC_COMM_WORLD, &dm);
2018        DMSetType(dm, DMPLEX);
2019        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2020        DMClone(dm, &sectiondm);
2021        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2022        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2023        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2024        PetscSectionSetChart(section, pStart, pEnd);
2025        PetscSectionSetUp(section);
2026        DMSetLocalSection(sectiondm, section);
2027        PetscSectionDestroy(&section);
2028        DMGetGlobalVector(sectiondm, &vec);
2029        PetscObjectSetName((PetscObject)vec, "vec_name");
2030        DMPlexTopologyView(dm, viewer);
2031        DMPlexSectionView(dm, viewer, sectiondm);
2032        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2033        DMRestoreGlobalVector(sectiondm, &vec);
2034        DMDestroy(&sectiondm);
2035        DMDestroy(&dm);
2036 .ve
2037 
2038 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2039 @*/
2040 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2041 {
2042   PetscBool ishdf5;
2043 
2044   PetscFunctionBegin;
2045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2046   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2047   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2048   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2049   /* Check consistency */
2050   {
2051     PetscSection section;
2052     PetscBool    includesConstraints;
2053     PetscInt     m, m1;
2054 
2055     PetscCall(VecGetLocalSize(vec, &m1));
2056     PetscCall(DMGetGlobalSection(sectiondm, &section));
2057     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2058     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2059     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2060     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2061   }
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2063   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2064   if (ishdf5) {
2065 #if defined(PETSC_HAVE_HDF5)
2066     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2067 #else
2068     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2069 #endif
2070   }
2071   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2072   PetscFunctionReturn(0);
2073 }
2074 
2075 /*@
2076   DMPlexLocalVectorView - Saves a local vector
2077 
2078   Collective on dm
2079 
2080   Input Parameters:
2081 + dm        - The `DM` that represents the topology
2082 . viewer    - The `PetscViewer` to save data with
2083 . sectiondm - The `DM` that contains the local section on which vec is defined; may be the same as dm
2084 - vec       - The local vector to be saved
2085 
2086   Level: advanced
2087 
2088   Note:
2089   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.
2090 
2091   Typical calling sequence:
2092 .vb
2093        DMCreate(PETSC_COMM_WORLD, &dm);
2094        DMSetType(dm, DMPLEX);
2095        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2096        DMClone(dm, &sectiondm);
2097        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2098        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2099        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2100        PetscSectionSetChart(section, pStart, pEnd);
2101        PetscSectionSetUp(section);
2102        DMSetLocalSection(sectiondm, section);
2103        DMGetLocalVector(sectiondm, &vec);
2104        PetscObjectSetName((PetscObject)vec, "vec_name");
2105        DMPlexTopologyView(dm, viewer);
2106        DMPlexSectionView(dm, viewer, sectiondm);
2107        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2108        DMRestoreLocalVector(sectiondm, &vec);
2109        DMDestroy(&sectiondm);
2110        DMDestroy(&dm);
2111 .ve
2112 
2113 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2114 @*/
2115 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2116 {
2117   PetscBool ishdf5;
2118 
2119   PetscFunctionBegin;
2120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2121   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2122   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2123   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2124   /* Check consistency */
2125   {
2126     PetscSection section;
2127     PetscBool    includesConstraints;
2128     PetscInt     m, m1;
2129 
2130     PetscCall(VecGetLocalSize(vec, &m1));
2131     PetscCall(DMGetLocalSection(sectiondm, &section));
2132     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2133     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2134     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2135     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2136   }
2137   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2138   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2139   if (ishdf5) {
2140 #if defined(PETSC_HAVE_HDF5)
2141     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2142 #else
2143     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2144 #endif
2145   }
2146   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2147   PetscFunctionReturn(0);
2148 }
2149 
2150 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2151 {
2152   PetscBool ishdf5;
2153 
2154   PetscFunctionBegin;
2155   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2156   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2157   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2158   if (ishdf5) {
2159 #if defined(PETSC_HAVE_HDF5)
2160     PetscViewerFormat format;
2161     PetscCall(PetscViewerGetFormat(viewer, &format));
2162     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2163       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2164     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2165       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2166     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2167     PetscFunctionReturn(0);
2168 #else
2169     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2170 #endif
2171   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2172 }
2173 
2174 /*@
2175   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2176 
2177   Collective on dm
2178 
2179   Input Parameters:
2180 + dm                - The `DM` into which the topology is loaded
2181 - viewer            - The `PetscViewer` for the saved topology
2182 
2183   Output Parameters:
2184 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
2185 
2186   Level: advanced
2187 
2188 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2189           `PetscViewer`, `PetscSF`
2190 @*/
2191 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2192 {
2193   PetscBool ishdf5;
2194 
2195   PetscFunctionBegin;
2196   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2197   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2198   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2199   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2200   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2201   if (ishdf5) {
2202 #if defined(PETSC_HAVE_HDF5)
2203     PetscViewerFormat format;
2204     PetscCall(PetscViewerGetFormat(viewer, &format));
2205     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2206       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2207     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2208 #else
2209     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2210 #endif
2211   }
2212   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2213   PetscFunctionReturn(0);
2214 }
2215 
2216 /*@
2217   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2218 
2219   Collective on dm
2220 
2221   Input Parameters:
2222 + dm     - The `DM` into which the coordinates are loaded
2223 . viewer - The `PetscViewer` for the saved coordinates
2224 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2225 
2226   Level: advanced
2227 
2228 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2229           `PetscSF`, `PetscViewer`
2230 @*/
2231 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2232 {
2233   PetscBool ishdf5;
2234 
2235   PetscFunctionBegin;
2236   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2237   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2238   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2239   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2240   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2241   if (ishdf5) {
2242 #if defined(PETSC_HAVE_HDF5)
2243     PetscViewerFormat format;
2244     PetscCall(PetscViewerGetFormat(viewer, &format));
2245     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2246       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2247     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2248 #else
2249     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2250 #endif
2251   }
2252   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2253   PetscFunctionReturn(0);
2254 }
2255 
2256 /*@
2257   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2258 
2259   Collective on dm
2260 
2261   Input Parameters:
2262 + dm     - The `DM` into which the labels are loaded
2263 . viewer - The `PetscViewer` for the saved labels
2264 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2265 
2266   Level: advanced
2267 
2268   Note:
2269   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2270 
2271 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2272           `PetscSF`, `PetscViewer`
2273 @*/
2274 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2275 {
2276   PetscBool ishdf5;
2277 
2278   PetscFunctionBegin;
2279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2280   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2281   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2282   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2283   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2284   if (ishdf5) {
2285 #if defined(PETSC_HAVE_HDF5)
2286     PetscViewerFormat format;
2287 
2288     PetscCall(PetscViewerGetFormat(viewer, &format));
2289     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2290       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2291     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2292 #else
2293     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2294 #endif
2295   }
2296   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2297   PetscFunctionReturn(0);
2298 }
2299 
2300 /*@
2301   DMPlexSectionLoad - Loads section into a `DMPLEX`
2302 
2303   Collective on dm
2304 
2305   Input Parameters:
2306 + dm          - The `DM` that represents the topology
2307 . viewer      - The `PetscViewer` that represents the on-disk section (sectionA)
2308 . sectiondm   - The `DM` into which the on-disk section (sectionA) is migrated
2309 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2310 
2311   Output Parameters
2312 + globalDofSF - The SF 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)
2313 - localDofSF  - The SF 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)
2314 
2315   Level: advanced
2316 
2317   Notes:
2318   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.
2319 
2320   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.
2321 
2322   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.
2323 
2324   Example using 2 processes:
2325 .vb
2326   NX (number of points on dm): 4
2327   sectionA                   : the on-disk section
2328   vecA                       : a vector associated with sectionA
2329   sectionB                   : sectiondm's local section constructed in this function
2330   vecB (local)               : a vector associated with sectiondm's local section
2331   vecB (global)              : a vector associated with sectiondm's global section
2332 
2333                                      rank 0    rank 1
2334   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2335   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2336   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2337   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2338   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2339   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2340   sectionB->atlasDof             :     1 0 1 | 1 3
2341   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2342   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2343   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2344 .ve
2345   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2346 
2347 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2348 @*/
2349 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2350 {
2351   PetscBool ishdf5;
2352 
2353   PetscFunctionBegin;
2354   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2355   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2356   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2357   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2358   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2359   if (localDofSF) PetscValidPointer(localDofSF, 6);
2360   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2361   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2362   if (ishdf5) {
2363 #if defined(PETSC_HAVE_HDF5)
2364     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2365 #else
2366     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2367 #endif
2368   }
2369   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2370   PetscFunctionReturn(0);
2371 }
2372 
2373 /*@
2374   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2375 
2376   Collective on dm
2377 
2378   Input Parameters:
2379 + dm        - The `DM` that represents the topology
2380 . viewer    - The `PetscViewer` that represents the on-disk vector data
2381 . sectiondm - The `DM` that contains the global section on which vec is defined
2382 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2383 - vec       - The global vector to set values of
2384 
2385   Level: advanced
2386 
2387   Notes:
2388   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.
2389 
2390   Typical calling sequence:
2391 .vb
2392        DMCreate(PETSC_COMM_WORLD, &dm);
2393        DMSetType(dm, DMPLEX);
2394        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2395        DMPlexTopologyLoad(dm, viewer, &sfX);
2396        DMClone(dm, &sectiondm);
2397        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2398        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2399        DMGetGlobalVector(sectiondm, &vec);
2400        PetscObjectSetName((PetscObject)vec, "vec_name");
2401        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2402        DMRestoreGlobalVector(sectiondm, &vec);
2403        PetscSFDestroy(&gsf);
2404        PetscSFDestroy(&sfX);
2405        DMDestroy(&sectiondm);
2406        DMDestroy(&dm);
2407 .ve
2408 
2409 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2410           `PetscSF`, `PetscViewer`
2411 @*/
2412 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2413 {
2414   PetscBool ishdf5;
2415 
2416   PetscFunctionBegin;
2417   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2418   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2419   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2420   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2421   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2422   /* Check consistency */
2423   {
2424     PetscSection section;
2425     PetscBool    includesConstraints;
2426     PetscInt     m, m1;
2427 
2428     PetscCall(VecGetLocalSize(vec, &m1));
2429     PetscCall(DMGetGlobalSection(sectiondm, &section));
2430     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2431     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2432     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2433     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2434   }
2435   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2436   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2437   if (ishdf5) {
2438 #if defined(PETSC_HAVE_HDF5)
2439     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2440 #else
2441     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2442 #endif
2443   }
2444   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2445   PetscFunctionReturn(0);
2446 }
2447 
2448 /*@
2449   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2450 
2451   Collective on dm
2452 
2453   Input Parameters:
2454 + dm        - The `DM` that represents the topology
2455 . viewer    - The `PetscViewer` that represents the on-disk vector data
2456 . sectiondm - The `DM` that contains the local section on which vec is defined
2457 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2458 - vec       - The local vector to set values of
2459 
2460   Level: advanced
2461 
2462   Notes:
2463   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.
2464 
2465   Typical calling sequence:
2466 .vb
2467        DMCreate(PETSC_COMM_WORLD, &dm);
2468        DMSetType(dm, DMPLEX);
2469        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2470        DMPlexTopologyLoad(dm, viewer, &sfX);
2471        DMClone(dm, &sectiondm);
2472        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2473        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2474        DMGetLocalVector(sectiondm, &vec);
2475        PetscObjectSetName((PetscObject)vec, "vec_name");
2476        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2477        DMRestoreLocalVector(sectiondm, &vec);
2478        PetscSFDestroy(&lsf);
2479        PetscSFDestroy(&sfX);
2480        DMDestroy(&sectiondm);
2481        DMDestroy(&dm);
2482 .ve
2483 
2484 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2485           `PetscSF`, `PetscViewer`
2486 @*/
2487 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2488 {
2489   PetscBool ishdf5;
2490 
2491   PetscFunctionBegin;
2492   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2493   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2494   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2495   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2496   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2497   /* Check consistency */
2498   {
2499     PetscSection section;
2500     PetscBool    includesConstraints;
2501     PetscInt     m, m1;
2502 
2503     PetscCall(VecGetLocalSize(vec, &m1));
2504     PetscCall(DMGetLocalSection(sectiondm, &section));
2505     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2506     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2507     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2508     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2509   }
2510   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2511   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2512   if (ishdf5) {
2513 #if defined(PETSC_HAVE_HDF5)
2514     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2515 #else
2516     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2517 #endif
2518   }
2519   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2520   PetscFunctionReturn(0);
2521 }
2522 
2523 PetscErrorCode DMDestroy_Plex(DM dm)
2524 {
2525   DM_Plex *mesh = (DM_Plex *)dm->data;
2526 
2527   PetscFunctionBegin;
2528   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2529   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2530   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2531   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2532   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2533   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2534   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2535   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2536   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2537   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2538   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2539   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2540   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2541   if (--mesh->refct > 0) PetscFunctionReturn(0);
2542   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2543   PetscCall(PetscFree(mesh->cones));
2544   PetscCall(PetscFree(mesh->coneOrientations));
2545   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2546   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2547   PetscCall(PetscFree(mesh->supports));
2548   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2549   PetscCall(PetscFree(mesh->facesTmp));
2550   PetscCall(PetscFree(mesh->tetgenOpts));
2551   PetscCall(PetscFree(mesh->triangleOpts));
2552   PetscCall(PetscFree(mesh->transformType));
2553   PetscCall(PetscFree(mesh->distributionName));
2554   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2555   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2556   PetscCall(ISDestroy(&mesh->subpointIS));
2557   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2558   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2559   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2560   PetscCall(ISDestroy(&mesh->anchorIS));
2561   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2562   PetscCall(PetscFree(mesh->parents));
2563   PetscCall(PetscFree(mesh->childIDs));
2564   PetscCall(PetscSectionDestroy(&mesh->childSection));
2565   PetscCall(PetscFree(mesh->children));
2566   PetscCall(DMDestroy(&mesh->referenceTree));
2567   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2568   PetscCall(PetscFree(mesh->neighbors));
2569   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2570   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2571   PetscCall(PetscFree(mesh));
2572   PetscFunctionReturn(0);
2573 }
2574 
2575 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2576 {
2577   PetscSection           sectionGlobal;
2578   PetscInt               bs = -1, mbs;
2579   PetscInt               localSize, localStart = 0;
2580   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2581   MatType                mtype;
2582   ISLocalToGlobalMapping ltog;
2583 
2584   PetscFunctionBegin;
2585   PetscCall(MatInitializePackage());
2586   mtype = dm->mattype;
2587   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2588   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2589   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2590   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2591   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2592   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2593   PetscCall(MatSetType(*J, mtype));
2594   PetscCall(MatSetFromOptions(*J));
2595   PetscCall(MatGetBlockSize(*J, &mbs));
2596   if (mbs > 1) bs = mbs;
2597   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2598   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2599   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2600   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2601   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2602   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2603   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2604   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2605   if (!isShell) {
2606     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2607     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2608     PetscInt  pStart, pEnd, p, dof, cdof;
2609 
2610     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2611 
2612     PetscCall(PetscCalloc1(localSize, &pblocks));
2613     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2614     for (p = pStart; p < pEnd; ++p) {
2615       PetscInt bdof, offset;
2616 
2617       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2618       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2619       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2620       for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2621       dof  = dof < 0 ? -(dof + 1) : dof;
2622       bdof = cdof && (dof - cdof) ? 1 : dof;
2623       if (dof) {
2624         if (bs < 0) {
2625           bs = bdof;
2626         } else if (bs != bdof) {
2627           bs = 1;
2628         }
2629       }
2630     }
2631     /* Must have same blocksize on all procs (some might have no points) */
2632     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2633     bsLocal[1] = bs;
2634     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2635     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2636     else bs = bsMinMax[0];
2637     bs = PetscMax(1, bs);
2638     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2639     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2640       PetscCall(MatSetBlockSize(*J, bs));
2641       PetscCall(MatSetUp(*J));
2642     } else {
2643       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2644       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2645       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2646     }
2647     { // Consolidate blocks
2648       PetscInt nblocks = 0;
2649       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2650         if (pblocks[i] == 0) continue;
2651         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2652         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]);
2653       }
2654       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2655     }
2656     PetscCall(PetscFree(pblocks));
2657   }
2658   PetscCall(MatSetDM(*J, dm));
2659   PetscFunctionReturn(0);
2660 }
2661 
2662 /*@
2663   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2664 
2665   Not Collective
2666 
2667   Input Parameter:
2668 . mesh - The `DMPLEX`
2669 
2670   Output Parameters:
2671 . subsection - The subdomain section
2672 
2673   Level: developer
2674 
2675 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `PetscSection`
2676 @*/
2677 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2678 {
2679   DM_Plex *mesh = (DM_Plex *)dm->data;
2680 
2681   PetscFunctionBegin;
2682   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2683   if (!mesh->subdomainSection) {
2684     PetscSection section;
2685     PetscSF      sf;
2686 
2687     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2688     PetscCall(DMGetLocalSection(dm, &section));
2689     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2690     PetscCall(PetscSFDestroy(&sf));
2691   }
2692   *subsection = mesh->subdomainSection;
2693   PetscFunctionReturn(0);
2694 }
2695 
2696 /*@
2697   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2698 
2699   Not Collective
2700 
2701   Input Parameter:
2702 . mesh - The `DMPLEX`
2703 
2704   Output Parameters:
2705 + pStart - The first mesh point
2706 - pEnd   - The upper bound for mesh points
2707 
2708   Level: beginner
2709 
2710 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2711 @*/
2712 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2713 {
2714   DM_Plex *mesh = (DM_Plex *)dm->data;
2715 
2716   PetscFunctionBegin;
2717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2718   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2719   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2720   PetscFunctionReturn(0);
2721 }
2722 
2723 /*@
2724   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2725 
2726   Not Collective
2727 
2728   Input Parameters:
2729 + mesh - The `DMPLEX`
2730 . pStart - The first mesh point
2731 - pEnd   - The upper bound for mesh points
2732 
2733   Level: beginner
2734 
2735 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2736 @*/
2737 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2738 {
2739   DM_Plex *mesh = (DM_Plex *)dm->data;
2740 
2741   PetscFunctionBegin;
2742   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2743   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2744   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2745   PetscFunctionReturn(0);
2746 }
2747 
2748 /*@
2749   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2750 
2751   Not Collective
2752 
2753   Input Parameters:
2754 + mesh - The `DMPLEX`
2755 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2756 
2757   Output Parameter:
2758 . size - The cone size for point p
2759 
2760   Level: beginner
2761 
2762 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2763 @*/
2764 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2765 {
2766   DM_Plex *mesh = (DM_Plex *)dm->data;
2767 
2768   PetscFunctionBegin;
2769   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2770   PetscValidIntPointer(size, 3);
2771   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2772   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2773   PetscFunctionReturn(0);
2774 }
2775 
2776 /*@
2777   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2778 
2779   Not Collective
2780 
2781   Input Parameters:
2782 + mesh - The `DMPLEX`
2783 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2784 - size - The cone size for point p
2785 
2786   Level: beginner
2787 
2788   Note:
2789   This should be called after `DMPlexSetChart()`.
2790 
2791 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2792 @*/
2793 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2794 {
2795   DM_Plex *mesh = (DM_Plex *)dm->data;
2796 
2797   PetscFunctionBegin;
2798   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2799   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2800   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2801   PetscFunctionReturn(0);
2802 }
2803 
2804 /*@C
2805   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2806 
2807   Not Collective
2808 
2809   Input Parameters:
2810 + dm - The `DMPLEX`
2811 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2812 
2813   Output Parameter:
2814 . cone - An array of points which are on the in-edges for point p
2815 
2816   Level: beginner
2817 
2818   Fortran Note:
2819   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2820   `DMPlexRestoreCone()` is not needed/available in C.
2821 
2822 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2823 @*/
2824 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2825 {
2826   DM_Plex *mesh = (DM_Plex *)dm->data;
2827   PetscInt off;
2828 
2829   PetscFunctionBegin;
2830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2831   PetscValidPointer(cone, 3);
2832   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2833   *cone = &mesh->cones[off];
2834   PetscFunctionReturn(0);
2835 }
2836 
2837 /*@C
2838   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2839 
2840   Not Collective
2841 
2842   Input Parameters:
2843 + dm - The `DMPLEX`
2844 - p - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2845 
2846   Output Parameters:
2847 + pConesSection - `PetscSection` describing the layout of pCones
2848 - pCones - An array of points which are on the in-edges for the point set p
2849 
2850   Level: intermediate
2851 
2852 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2853 @*/
2854 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2855 {
2856   PetscSection cs, newcs;
2857   PetscInt    *cones;
2858   PetscInt    *newarr = NULL;
2859   PetscInt     n;
2860 
2861   PetscFunctionBegin;
2862   PetscCall(DMPlexGetCones(dm, &cones));
2863   PetscCall(DMPlexGetConeSection(dm, &cs));
2864   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2865   if (pConesSection) *pConesSection = newcs;
2866   if (pCones) {
2867     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2868     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2869   }
2870   PetscFunctionReturn(0);
2871 }
2872 
2873 /*@
2874   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2875 
2876   Not Collective
2877 
2878   Input Parameters:
2879 + dm - The `DMPLEX`
2880 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2881 
2882   Output Parameter:
2883 . expandedPoints - An array of vertices recursively expanded from input points
2884 
2885   Level: advanced
2886 
2887   Notes:
2888   Like `DMPlexGetConeRecursive()` but returns only the 0-depth IS (i.e. vertices only) and no sections.
2889 
2890   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2891 
2892 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2893           `DMPlexGetDepth()`, `IS`
2894 @*/
2895 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2896 {
2897   IS      *expandedPointsAll;
2898   PetscInt depth;
2899 
2900   PetscFunctionBegin;
2901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2902   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2903   PetscValidPointer(expandedPoints, 3);
2904   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2905   *expandedPoints = expandedPointsAll[0];
2906   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2907   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2908   PetscFunctionReturn(0);
2909 }
2910 
2911 /*@
2912   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).
2913 
2914   Not Collective
2915 
2916   Input Parameters:
2917 + dm - The `DMPLEX`
2918 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2919 
2920   Output Parameters:
2921 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2922 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2923 - sections - (optional) An array of sections which describe mappings from points to their cone points
2924 
2925   Level: advanced
2926 
2927   Notes:
2928   Like `DMPlexGetConeTuple()` but recursive.
2929 
2930   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.
2931   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2932 
2933   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:
2934   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2935   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2936 
2937 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2938           `DMPlexGetDepth()`, `PetscSection`, `IS`
2939 @*/
2940 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2941 {
2942   const PetscInt *arr0 = NULL, *cone = NULL;
2943   PetscInt       *arr = NULL, *newarr = NULL;
2944   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2945   IS             *expandedPoints_;
2946   PetscSection   *sections_;
2947 
2948   PetscFunctionBegin;
2949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2950   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2951   if (depth) PetscValidIntPointer(depth, 3);
2952   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2953   if (sections) PetscValidPointer(sections, 5);
2954   PetscCall(ISGetLocalSize(points, &n));
2955   PetscCall(ISGetIndices(points, &arr0));
2956   PetscCall(DMPlexGetDepth(dm, &depth_));
2957   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2958   PetscCall(PetscCalloc1(depth_, &sections_));
2959   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2960   for (d = depth_ - 1; d >= 0; d--) {
2961     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2962     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2963     for (i = 0; i < n; i++) {
2964       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2965       if (arr[i] >= start && arr[i] < end) {
2966         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2967         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2968       } else {
2969         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2970       }
2971     }
2972     PetscCall(PetscSectionSetUp(sections_[d]));
2973     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2974     PetscCall(PetscMalloc1(newn, &newarr));
2975     for (i = 0; i < n; i++) {
2976       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2977       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2978       if (cn > 1) {
2979         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2980         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2981       } else {
2982         newarr[co] = arr[i];
2983       }
2984     }
2985     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2986     arr = newarr;
2987     n   = newn;
2988   }
2989   PetscCall(ISRestoreIndices(points, &arr0));
2990   *depth = depth_;
2991   if (expandedPoints) *expandedPoints = expandedPoints_;
2992   else {
2993     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2994     PetscCall(PetscFree(expandedPoints_));
2995   }
2996   if (sections) *sections = sections_;
2997   else {
2998     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2999     PetscCall(PetscFree(sections_));
3000   }
3001   PetscFunctionReturn(0);
3002 }
3003 
3004 /*@
3005   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3006 
3007   Not Collective
3008 
3009   Input Parameters:
3010 + dm - The `DMPLEX`
3011 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3012 
3013   Output Parameters:
3014 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3015 . expandedPoints - (optional) An array of recursively expanded cones
3016 - sections - (optional) An array of sections which describe mappings from points to their cone points
3017 
3018   Level: advanced
3019 
3020   Note:
3021   See `DMPlexGetConeRecursive()`
3022 
3023 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3024           `DMPlexGetDepth()`, `IS`, `PetscSection`
3025 @*/
3026 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3027 {
3028   PetscInt d, depth_;
3029 
3030   PetscFunctionBegin;
3031   PetscCall(DMPlexGetDepth(dm, &depth_));
3032   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3033   if (depth) *depth = 0;
3034   if (expandedPoints) {
3035     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3036     PetscCall(PetscFree(*expandedPoints));
3037   }
3038   if (sections) {
3039     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3040     PetscCall(PetscFree(*sections));
3041   }
3042   PetscFunctionReturn(0);
3043 }
3044 
3045 /*@
3046   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
3047 
3048   Not Collective
3049 
3050   Input Parameters:
3051 + mesh - The `DMPLEX`
3052 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3053 - cone - An array of points which are on the in-edges for point p
3054 
3055   Level: beginner
3056 
3057   Note:
3058   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3059 
3060 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3061 @*/
3062 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3063 {
3064   DM_Plex *mesh = (DM_Plex *)dm->data;
3065   PetscInt pStart, pEnd;
3066   PetscInt dof, off, c;
3067 
3068   PetscFunctionBegin;
3069   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3070   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3071   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3072   if (dof) PetscValidIntPointer(cone, 3);
3073   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3074   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);
3075   for (c = 0; c < dof; ++c) {
3076     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);
3077     mesh->cones[off + c] = cone[c];
3078   }
3079   PetscFunctionReturn(0);
3080 }
3081 
3082 /*@C
3083   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3084 
3085   Not Collective
3086 
3087   Input Parameters:
3088 + mesh - The `DMPLEX`
3089 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3090 
3091   Output Parameter:
3092 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3093                     integer giving the prescription for cone traversal.
3094 
3095   Level: beginner
3096 
3097   Note:
3098   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3099   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3100   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3101   with the identity.
3102 
3103   Fortran Note:
3104   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3105   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3106 
3107 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3108 @*/
3109 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3110 {
3111   DM_Plex *mesh = (DM_Plex *)dm->data;
3112   PetscInt off;
3113 
3114   PetscFunctionBegin;
3115   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3116   if (PetscDefined(USE_DEBUG)) {
3117     PetscInt dof;
3118     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3119     if (dof) PetscValidPointer(coneOrientation, 3);
3120   }
3121   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3122 
3123   *coneOrientation = &mesh->coneOrientations[off];
3124   PetscFunctionReturn(0);
3125 }
3126 
3127 /*@
3128   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3129 
3130   Not Collective
3131 
3132   Input Parameters:
3133 + mesh - The `DMPLEX`
3134 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3135 - coneOrientation - An array of orientations
3136 
3137   Level: beginner
3138 
3139   Notes:
3140   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3141 
3142   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3143 
3144 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3145 @*/
3146 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3147 {
3148   DM_Plex *mesh = (DM_Plex *)dm->data;
3149   PetscInt pStart, pEnd;
3150   PetscInt dof, off, c;
3151 
3152   PetscFunctionBegin;
3153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3154   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3155   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3156   if (dof) PetscValidIntPointer(coneOrientation, 3);
3157   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3158   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);
3159   for (c = 0; c < dof; ++c) {
3160     PetscInt cdof, o = coneOrientation[c];
3161 
3162     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3163     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);
3164     mesh->coneOrientations[off + c] = o;
3165   }
3166   PetscFunctionReturn(0);
3167 }
3168 
3169 /*@
3170   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3171 
3172   Not Collective
3173 
3174   Input Parameters:
3175 + mesh - The `DMPLEX`
3176 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3177 . conePos - The local index in the cone where the point should be put
3178 - conePoint - The mesh point to insert
3179 
3180   Level: beginner
3181 
3182 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3183 @*/
3184 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3185 {
3186   DM_Plex *mesh = (DM_Plex *)dm->data;
3187   PetscInt pStart, pEnd;
3188   PetscInt dof, off;
3189 
3190   PetscFunctionBegin;
3191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3192   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3193   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);
3194   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);
3195   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3196   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3197   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);
3198   mesh->cones[off + conePos] = conePoint;
3199   PetscFunctionReturn(0);
3200 }
3201 
3202 /*@
3203   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3204 
3205   Not Collective
3206 
3207   Input Parameters:
3208 + mesh - The `DMPLEX`
3209 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3210 . conePos - The local index in the cone where the point should be put
3211 - coneOrientation - The point orientation to insert
3212 
3213   Level: beginner
3214 
3215   Note:
3216   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3217 
3218 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3219 @*/
3220 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3221 {
3222   DM_Plex *mesh = (DM_Plex *)dm->data;
3223   PetscInt pStart, pEnd;
3224   PetscInt dof, off;
3225 
3226   PetscFunctionBegin;
3227   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3228   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3229   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);
3230   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3231   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3232   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);
3233   mesh->coneOrientations[off + conePos] = coneOrientation;
3234   PetscFunctionReturn(0);
3235 }
3236 
3237 /*@C
3238   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3239 
3240   Not collective
3241 
3242   Input Parameters:
3243 + dm - The DMPlex
3244 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3245 
3246   Output Parameters:
3247 + cone - An array of points which are on the in-edges for point p
3248 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3249         integer giving the prescription for cone traversal.
3250 
3251   Level: beginner
3252 
3253   Notes:
3254   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3255   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3256   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3257   with the identity.
3258 
3259   Fortran Notes:
3260   Since it returns an array, this routine is only available in Fortran 90, and you must
3261   include petsc.h90 in your code.
3262   You must also call DMPlexRestoreCone() after you finish using the returned array.
3263   DMPlexRestoreCone() is not needed/available in C.
3264 
3265 .seealso: `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3266 @*/
3267 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3268 {
3269   DM_Plex *mesh = (DM_Plex *)dm->data;
3270 
3271   PetscFunctionBegin;
3272   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3273   if (mesh->tr) {
3274     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3275   } else {
3276     PetscInt off;
3277     if (PetscDefined(USE_DEBUG)) {
3278       PetscInt dof;
3279       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3280       if (dof) {
3281         if (cone) PetscValidPointer(cone, 3);
3282         if (ornt) PetscValidPointer(ornt, 4);
3283       }
3284     }
3285     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3286     if (cone) *cone = &mesh->cones[off];
3287     if (ornt) *ornt = &mesh->coneOrientations[off];
3288   }
3289   PetscFunctionReturn(0);
3290 }
3291 
3292 /*@C
3293   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3294 
3295   Not collective
3296 
3297   Input Parameters:
3298 + dm - The DMPlex
3299 . p  - The point, which must lie in the chart set with DMPlexSetChart()
3300 . cone - An array of points which are on the in-edges for point p
3301 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3302         integer giving the prescription for cone traversal.
3303 
3304   Level: beginner
3305 
3306   Notes:
3307   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3308   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3309   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3310   with the identity.
3311 
3312   Fortran Notes:
3313   Since it returns an array, this routine is only available in Fortran 90, and you must
3314   include petsc.h90 in your code.
3315   You must also call DMPlexRestoreCone() after you finish using the returned array.
3316   DMPlexRestoreCone() is not needed/available in C.
3317 
3318 .seealso: `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3319 @*/
3320 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3321 {
3322   DM_Plex *mesh = (DM_Plex *)dm->data;
3323 
3324   PetscFunctionBegin;
3325   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3326   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3327   PetscFunctionReturn(0);
3328 }
3329 
3330 /*@
3331   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3332 
3333   Not Collective
3334 
3335   Input Parameters:
3336 + mesh - The `DMPLEX`
3337 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3338 
3339   Output Parameter:
3340 . size - The support size for point p
3341 
3342   Level: beginner
3343 
3344 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3345 @*/
3346 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3347 {
3348   DM_Plex *mesh = (DM_Plex *)dm->data;
3349 
3350   PetscFunctionBegin;
3351   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3352   PetscValidIntPointer(size, 3);
3353   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3354   PetscFunctionReturn(0);
3355 }
3356 
3357 /*@
3358   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3359 
3360   Not Collective
3361 
3362   Input Parameters:
3363 + mesh - The `DMPLEX`
3364 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3365 - size - The support size for point p
3366 
3367   Level: beginner
3368 
3369   Note:
3370   This should be called after DMPlexSetChart().
3371 
3372 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3373 @*/
3374 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3375 {
3376   DM_Plex *mesh = (DM_Plex *)dm->data;
3377 
3378   PetscFunctionBegin;
3379   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3380   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3381   PetscFunctionReturn(0);
3382 }
3383 
3384 /*@C
3385   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3386 
3387   Not Collective
3388 
3389   Input Parameters:
3390 + mesh - The `DMPLEX`
3391 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3392 
3393   Output Parameter:
3394 . support - An array of points which are on the out-edges for point p
3395 
3396   Level: beginner
3397 
3398   Fortran Note:
3399   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3400   `DMPlexRestoreSupport()` is not needed/available in C.
3401 
3402 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3403 @*/
3404 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3405 {
3406   DM_Plex *mesh = (DM_Plex *)dm->data;
3407   PetscInt off;
3408 
3409   PetscFunctionBegin;
3410   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3411   PetscValidPointer(support, 3);
3412   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3413   *support = &mesh->supports[off];
3414   PetscFunctionReturn(0);
3415 }
3416 
3417 /*@
3418   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3419 
3420   Not Collective
3421 
3422   Input Parameters:
3423 + mesh - The `DMPLEX`
3424 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3425 - support - An array of points which are on the out-edges for point p
3426 
3427   Level: beginner
3428 
3429   Note:
3430   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3431 
3432 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3433 @*/
3434 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3435 {
3436   DM_Plex *mesh = (DM_Plex *)dm->data;
3437   PetscInt pStart, pEnd;
3438   PetscInt dof, off, c;
3439 
3440   PetscFunctionBegin;
3441   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3442   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3443   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3444   if (dof) PetscValidIntPointer(support, 3);
3445   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3446   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);
3447   for (c = 0; c < dof; ++c) {
3448     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);
3449     mesh->supports[off + c] = support[c];
3450   }
3451   PetscFunctionReturn(0);
3452 }
3453 
3454 /*@
3455   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3456 
3457   Not Collective
3458 
3459   Input Parameters:
3460 + mesh - The `DMPLEX`
3461 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3462 . supportPos - The local index in the cone where the point should be put
3463 - supportPoint - The mesh point to insert
3464 
3465   Level: beginner
3466 
3467 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3468 @*/
3469 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3470 {
3471   DM_Plex *mesh = (DM_Plex *)dm->data;
3472   PetscInt pStart, pEnd;
3473   PetscInt dof, off;
3474 
3475   PetscFunctionBegin;
3476   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3477   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3478   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3479   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3480   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);
3481   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);
3482   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);
3483   mesh->supports[off + supportPos] = supportPoint;
3484   PetscFunctionReturn(0);
3485 }
3486 
3487 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3488 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3489 {
3490   switch (ct) {
3491   case DM_POLYTOPE_SEGMENT:
3492     if (o == -1) return -2;
3493     break;
3494   case DM_POLYTOPE_TRIANGLE:
3495     if (o == -3) return -1;
3496     if (o == -2) return -3;
3497     if (o == -1) return -2;
3498     break;
3499   case DM_POLYTOPE_QUADRILATERAL:
3500     if (o == -4) return -2;
3501     if (o == -3) return -1;
3502     if (o == -2) return -4;
3503     if (o == -1) return -3;
3504     break;
3505   default:
3506     return o;
3507   }
3508   return o;
3509 }
3510 
3511 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3512 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3513 {
3514   switch (ct) {
3515   case DM_POLYTOPE_SEGMENT:
3516     if ((o == -2) || (o == 1)) return -1;
3517     if (o == -1) return 0;
3518     break;
3519   case DM_POLYTOPE_TRIANGLE:
3520     if (o == -3) return -2;
3521     if (o == -2) return -1;
3522     if (o == -1) return -3;
3523     break;
3524   case DM_POLYTOPE_QUADRILATERAL:
3525     if (o == -4) return -2;
3526     if (o == -3) return -1;
3527     if (o == -2) return -4;
3528     if (o == -1) return -3;
3529     break;
3530   default:
3531     return o;
3532   }
3533   return o;
3534 }
3535 
3536 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3537 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3538 {
3539   PetscInt pStart, pEnd, p;
3540 
3541   PetscFunctionBegin;
3542   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3543   for (p = pStart; p < pEnd; ++p) {
3544     const PetscInt *cone, *ornt;
3545     PetscInt        coneSize, c;
3546 
3547     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3548     PetscCall(DMPlexGetCone(dm, p, &cone));
3549     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3550     for (c = 0; c < coneSize; ++c) {
3551       DMPolytopeType ct;
3552       const PetscInt o = ornt[c];
3553 
3554       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3555       switch (ct) {
3556       case DM_POLYTOPE_SEGMENT:
3557         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3558         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3559         break;
3560       case DM_POLYTOPE_TRIANGLE:
3561         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3562         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3563         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3564         break;
3565       case DM_POLYTOPE_QUADRILATERAL:
3566         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3567         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3568         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3569         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3570         break;
3571       default:
3572         break;
3573       }
3574     }
3575   }
3576   PetscFunctionReturn(0);
3577 }
3578 
3579 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3580 {
3581   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3582   PetscInt       *closure;
3583   const PetscInt *tmp = NULL, *tmpO = NULL;
3584   PetscInt        off = 0, tmpSize, t;
3585 
3586   PetscFunctionBeginHot;
3587   if (ornt) {
3588     PetscCall(DMPlexGetCellType(dm, p, &ct));
3589     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3590   }
3591   if (*points) {
3592     closure = *points;
3593   } else {
3594     PetscInt maxConeSize, maxSupportSize;
3595     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3596     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3597   }
3598   if (useCone) {
3599     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3600     PetscCall(DMPlexGetCone(dm, p, &tmp));
3601     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3602   } else {
3603     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3604     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3605   }
3606   if (ct == DM_POLYTOPE_UNKNOWN) {
3607     closure[off++] = p;
3608     closure[off++] = 0;
3609     for (t = 0; t < tmpSize; ++t) {
3610       closure[off++] = tmp[t];
3611       closure[off++] = tmpO ? tmpO[t] : 0;
3612     }
3613   } else {
3614     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3615 
3616     /* We assume that cells with a valid type have faces with a valid type */
3617     closure[off++] = p;
3618     closure[off++] = ornt;
3619     for (t = 0; t < tmpSize; ++t) {
3620       DMPolytopeType ft;
3621 
3622       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3623       closure[off++] = tmp[arr[t]];
3624       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3625     }
3626   }
3627   if (numPoints) *numPoints = tmpSize + 1;
3628   if (points) *points = closure;
3629   PetscFunctionReturn(0);
3630 }
3631 
3632 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3633 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3634 {
3635   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3636   const PetscInt *cone, *ornt;
3637   PetscInt       *pts, *closure = NULL;
3638   DMPolytopeType  ft;
3639   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3640   PetscInt        dim, coneSize, c, d, clSize, cl;
3641 
3642   PetscFunctionBeginHot;
3643   PetscCall(DMGetDimension(dm, &dim));
3644   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3645   PetscCall(DMPlexGetCone(dm, point, &cone));
3646   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3647   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3648   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3649   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3650   maxSize       = PetscMax(coneSeries, supportSeries);
3651   if (*points) {
3652     pts = *points;
3653   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3654   c        = 0;
3655   pts[c++] = point;
3656   pts[c++] = o;
3657   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3658   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3659   for (cl = 0; cl < clSize * 2; cl += 2) {
3660     pts[c++] = closure[cl];
3661     pts[c++] = closure[cl + 1];
3662   }
3663   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3664   for (cl = 0; cl < clSize * 2; cl += 2) {
3665     pts[c++] = closure[cl];
3666     pts[c++] = closure[cl + 1];
3667   }
3668   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3669   for (d = 2; d < coneSize; ++d) {
3670     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3671     pts[c++] = cone[arr[d * 2 + 0]];
3672     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3673   }
3674   if (dim >= 3) {
3675     for (d = 2; d < coneSize; ++d) {
3676       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3677       const PetscInt *fcone, *fornt;
3678       PetscInt        fconeSize, fc, i;
3679 
3680       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3681       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3682       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3683       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3684       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3685       for (fc = 0; fc < fconeSize; ++fc) {
3686         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3687         const PetscInt co = farr[fc * 2 + 1];
3688 
3689         for (i = 0; i < c; i += 2)
3690           if (pts[i] == cp) break;
3691         if (i == c) {
3692           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3693           pts[c++] = cp;
3694           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3695         }
3696       }
3697     }
3698   }
3699   *numPoints = c / 2;
3700   *points    = pts;
3701   PetscFunctionReturn(0);
3702 }
3703 
3704 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3705 {
3706   DMPolytopeType ct;
3707   PetscInt      *closure, *fifo;
3708   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3709   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3710   PetscInt       depth, maxSize;
3711 
3712   PetscFunctionBeginHot;
3713   PetscCall(DMPlexGetDepth(dm, &depth));
3714   if (depth == 1) {
3715     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3716     PetscFunctionReturn(0);
3717   }
3718   PetscCall(DMPlexGetCellType(dm, p, &ct));
3719   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3720   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3721     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3722     PetscFunctionReturn(0);
3723   }
3724   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3725   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3726   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3727   maxSize       = PetscMax(coneSeries, supportSeries);
3728   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3729   if (*points) {
3730     closure = *points;
3731   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3732   closure[closureSize++] = p;
3733   closure[closureSize++] = ornt;
3734   fifo[fifoSize++]       = p;
3735   fifo[fifoSize++]       = ornt;
3736   fifo[fifoSize++]       = ct;
3737   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3738   while (fifoSize - fifoStart) {
3739     const PetscInt       q    = fifo[fifoStart++];
3740     const PetscInt       o    = fifo[fifoStart++];
3741     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3742     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3743     const PetscInt      *tmp, *tmpO;
3744     PetscInt             tmpSize, t;
3745 
3746     if (PetscDefined(USE_DEBUG)) {
3747       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3748       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);
3749     }
3750     if (useCone) {
3751       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3752       PetscCall(DMPlexGetCone(dm, q, &tmp));
3753       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3754     } else {
3755       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3756       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3757       tmpO = NULL;
3758     }
3759     for (t = 0; t < tmpSize; ++t) {
3760       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3761       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3762       const PetscInt cp = tmp[ip];
3763       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3764       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3765       PetscInt       c;
3766 
3767       /* Check for duplicate */
3768       for (c = 0; c < closureSize; c += 2) {
3769         if (closure[c] == cp) break;
3770       }
3771       if (c == closureSize) {
3772         closure[closureSize++] = cp;
3773         closure[closureSize++] = co;
3774         fifo[fifoSize++]       = cp;
3775         fifo[fifoSize++]       = co;
3776         fifo[fifoSize++]       = ct;
3777       }
3778     }
3779   }
3780   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3781   if (numPoints) *numPoints = closureSize / 2;
3782   if (points) *points = closure;
3783   PetscFunctionReturn(0);
3784 }
3785 
3786 /*@C
3787   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3788 
3789   Not Collective
3790 
3791   Input Parameters:
3792 + dm      - The `DMPLEX`
3793 . p       - The mesh point
3794 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3795 
3796   Input/Output Parameter:
3797 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3798            if NULL on input, internal storage will be returned, otherwise the provided array is used
3799 
3800   Output Parameter:
3801 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3802 
3803   Level: beginner
3804 
3805   Note:
3806   If using internal storage (points is NULL on input), each call overwrites the last output.
3807 
3808   Fortran Note:
3809   The numPoints argument is not present in the Fortran binding since it is internal to the array.
3810 
3811 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3812 @*/
3813 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3814 {
3815   PetscFunctionBeginHot;
3816   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3817   if (numPoints) PetscValidIntPointer(numPoints, 4);
3818   if (points) PetscValidPointer(points, 5);
3819   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3820   PetscFunctionReturn(0);
3821 }
3822 
3823 /*@C
3824   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3825 
3826   Not Collective
3827 
3828   Input Parameters:
3829 + dm        - The `DMPLEX`
3830 . p         - The mesh point
3831 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3832 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3833 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3834 
3835   Level: beginner
3836 
3837   Note:
3838   If not using internal storage (points is not NULL on input), this call is unnecessary
3839 
3840 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3841 @*/
3842 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3843 {
3844   PetscFunctionBeginHot;
3845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3846   if (numPoints) *numPoints = 0;
3847   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3848   PetscFunctionReturn(0);
3849 }
3850 
3851 /*@
3852   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3853 
3854   Not Collective
3855 
3856   Input Parameter:
3857 . mesh - The `DMPLEX`
3858 
3859   Output Parameters:
3860 + maxConeSize - The maximum number of in-edges
3861 - maxSupportSize - The maximum number of out-edges
3862 
3863   Level: beginner
3864 
3865 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3866 @*/
3867 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3868 {
3869   DM_Plex *mesh = (DM_Plex *)dm->data;
3870 
3871   PetscFunctionBegin;
3872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3873   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3874   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3875   PetscFunctionReturn(0);
3876 }
3877 
3878 PetscErrorCode DMSetUp_Plex(DM dm)
3879 {
3880   DM_Plex *mesh = (DM_Plex *)dm->data;
3881   PetscInt size, maxSupportSize;
3882 
3883   PetscFunctionBegin;
3884   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3885   PetscCall(PetscSectionSetUp(mesh->coneSection));
3886   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3887   PetscCall(PetscMalloc1(size, &mesh->cones));
3888   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3889   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3890   if (maxSupportSize) {
3891     PetscCall(PetscSectionSetUp(mesh->supportSection));
3892     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3893     PetscCall(PetscMalloc1(size, &mesh->supports));
3894   }
3895   PetscFunctionReturn(0);
3896 }
3897 
3898 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3899 {
3900   PetscFunctionBegin;
3901   if (subdm) PetscCall(DMClone(dm, subdm));
3902   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3903   if (subdm) (*subdm)->useNatural = dm->useNatural;
3904   if (dm->useNatural && dm->sfMigration) {
3905     PetscSF sfNatural;
3906 
3907     (*subdm)->sfMigration = dm->sfMigration;
3908     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3909     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3910     (*subdm)->sfNatural = sfNatural;
3911   }
3912   PetscFunctionReturn(0);
3913 }
3914 
3915 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3916 {
3917   PetscInt i = 0;
3918 
3919   PetscFunctionBegin;
3920   PetscCall(DMClone(dms[0], superdm));
3921   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3922   (*superdm)->useNatural = PETSC_FALSE;
3923   for (i = 0; i < len; i++) {
3924     if (dms[i]->useNatural && dms[i]->sfMigration) {
3925       PetscSF sfNatural;
3926 
3927       (*superdm)->sfMigration = dms[i]->sfMigration;
3928       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3929       (*superdm)->useNatural = PETSC_TRUE;
3930       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3931       (*superdm)->sfNatural = sfNatural;
3932       break;
3933     }
3934   }
3935   PetscFunctionReturn(0);
3936 }
3937 
3938 /*@
3939   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3940 
3941   Not Collective
3942 
3943   Input Parameter:
3944 . mesh - The `DMPLEX`
3945 
3946   Level: beginner
3947 
3948   Note:
3949   This should be called after all calls to `DMPlexSetCone()`
3950 
3951 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3952 @*/
3953 PetscErrorCode DMPlexSymmetrize(DM dm)
3954 {
3955   DM_Plex  *mesh = (DM_Plex *)dm->data;
3956   PetscInt *offsets;
3957   PetscInt  supportSize;
3958   PetscInt  pStart, pEnd, p;
3959 
3960   PetscFunctionBegin;
3961   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3962   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3963   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3964   /* Calculate support sizes */
3965   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3966   for (p = pStart; p < pEnd; ++p) {
3967     PetscInt dof, off, c;
3968 
3969     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3970     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3971     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3972   }
3973   PetscCall(PetscSectionSetUp(mesh->supportSection));
3974   /* Calculate supports */
3975   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3976   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3977   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3978   for (p = pStart; p < pEnd; ++p) {
3979     PetscInt dof, off, c;
3980 
3981     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3982     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3983     for (c = off; c < off + dof; ++c) {
3984       const PetscInt q = mesh->cones[c];
3985       PetscInt       offS;
3986 
3987       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3988 
3989       mesh->supports[offS + offsets[q]] = p;
3990       ++offsets[q];
3991     }
3992   }
3993   PetscCall(PetscFree(offsets));
3994   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
3995   PetscFunctionReturn(0);
3996 }
3997 
3998 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3999 {
4000   IS stratumIS;
4001 
4002   PetscFunctionBegin;
4003   if (pStart >= pEnd) PetscFunctionReturn(0);
4004   if (PetscDefined(USE_DEBUG)) {
4005     PetscInt  qStart, qEnd, numLevels, level;
4006     PetscBool overlap = PETSC_FALSE;
4007     PetscCall(DMLabelGetNumValues(label, &numLevels));
4008     for (level = 0; level < numLevels; level++) {
4009       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4010       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4011         overlap = PETSC_TRUE;
4012         break;
4013       }
4014     }
4015     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);
4016   }
4017   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4018   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4019   PetscCall(ISDestroy(&stratumIS));
4020   PetscFunctionReturn(0);
4021 }
4022 
4023 /*@
4024   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4025   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4026   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4027   the DAG.
4028 
4029   Collective on dm
4030 
4031   Input Parameter:
4032 . mesh - The `DMPLEX`
4033 
4034   Level: beginner
4035 
4036   Notes:
4037   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4038   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4039   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4040   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4041   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4042 
4043   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4044   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4045   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
4046   to interpolate only that one (e0), so that
4047 .vb
4048   cone(c0) = {e0, v2}
4049   cone(e0) = {v0, v1}
4050 .ve
4051   If `DMPlexStratify()` is run on this mesh, it will give depths
4052 .vb
4053    depth 0 = {v0, v1, v2}
4054    depth 1 = {e0, c0}
4055 .ve
4056   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4057 
4058   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4059 
4060 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4061 @*/
4062 PetscErrorCode DMPlexStratify(DM dm)
4063 {
4064   DM_Plex *mesh = (DM_Plex *)dm->data;
4065   DMLabel  label;
4066   PetscInt pStart, pEnd, p;
4067   PetscInt numRoots = 0, numLeaves = 0;
4068 
4069   PetscFunctionBegin;
4070   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4071   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4072 
4073   /* Create depth label */
4074   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4075   PetscCall(DMCreateLabel(dm, "depth"));
4076   PetscCall(DMPlexGetDepthLabel(dm, &label));
4077 
4078   {
4079     /* Initialize roots and count leaves */
4080     PetscInt sMin = PETSC_MAX_INT;
4081     PetscInt sMax = PETSC_MIN_INT;
4082     PetscInt coneSize, supportSize;
4083 
4084     for (p = pStart; p < pEnd; ++p) {
4085       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4086       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4087       if (!coneSize && supportSize) {
4088         sMin = PetscMin(p, sMin);
4089         sMax = PetscMax(p, sMax);
4090         ++numRoots;
4091       } else if (!supportSize && coneSize) {
4092         ++numLeaves;
4093       } else if (!supportSize && !coneSize) {
4094         /* Isolated points */
4095         sMin = PetscMin(p, sMin);
4096         sMax = PetscMax(p, sMax);
4097       }
4098     }
4099     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4100   }
4101 
4102   if (numRoots + numLeaves == (pEnd - pStart)) {
4103     PetscInt sMin = PETSC_MAX_INT;
4104     PetscInt sMax = PETSC_MIN_INT;
4105     PetscInt coneSize, supportSize;
4106 
4107     for (p = pStart; p < pEnd; ++p) {
4108       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4109       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4110       if (!supportSize && coneSize) {
4111         sMin = PetscMin(p, sMin);
4112         sMax = PetscMax(p, sMax);
4113       }
4114     }
4115     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4116   } else {
4117     PetscInt level = 0;
4118     PetscInt qStart, qEnd, q;
4119 
4120     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4121     while (qEnd > qStart) {
4122       PetscInt sMin = PETSC_MAX_INT;
4123       PetscInt sMax = PETSC_MIN_INT;
4124 
4125       for (q = qStart; q < qEnd; ++q) {
4126         const PetscInt *support;
4127         PetscInt        supportSize, s;
4128 
4129         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4130         PetscCall(DMPlexGetSupport(dm, q, &support));
4131         for (s = 0; s < supportSize; ++s) {
4132           sMin = PetscMin(support[s], sMin);
4133           sMax = PetscMax(support[s], sMax);
4134         }
4135       }
4136       PetscCall(DMLabelGetNumValues(label, &level));
4137       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4138       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4139     }
4140   }
4141   { /* just in case there is an empty process */
4142     PetscInt numValues, maxValues = 0, v;
4143 
4144     PetscCall(DMLabelGetNumValues(label, &numValues));
4145     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4146     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4147   }
4148   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4149   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4150   PetscFunctionReturn(0);
4151 }
4152 
4153 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4154 {
4155   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4156   PetscInt       dim, depth, pheight, coneSize;
4157 
4158   PetscFunctionBeginHot;
4159   PetscCall(DMGetDimension(dm, &dim));
4160   PetscCall(DMPlexGetDepth(dm, &depth));
4161   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4162   pheight = depth - pdepth;
4163   if (depth <= 1) {
4164     switch (pdepth) {
4165     case 0:
4166       ct = DM_POLYTOPE_POINT;
4167       break;
4168     case 1:
4169       switch (coneSize) {
4170       case 2:
4171         ct = DM_POLYTOPE_SEGMENT;
4172         break;
4173       case 3:
4174         ct = DM_POLYTOPE_TRIANGLE;
4175         break;
4176       case 4:
4177         switch (dim) {
4178         case 2:
4179           ct = DM_POLYTOPE_QUADRILATERAL;
4180           break;
4181         case 3:
4182           ct = DM_POLYTOPE_TETRAHEDRON;
4183           break;
4184         default:
4185           break;
4186         }
4187         break;
4188       case 5:
4189         ct = DM_POLYTOPE_PYRAMID;
4190         break;
4191       case 6:
4192         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4193         break;
4194       case 8:
4195         ct = DM_POLYTOPE_HEXAHEDRON;
4196         break;
4197       default:
4198         break;
4199       }
4200     }
4201   } else {
4202     if (pdepth == 0) {
4203       ct = DM_POLYTOPE_POINT;
4204     } else if (pheight == 0) {
4205       switch (dim) {
4206       case 1:
4207         switch (coneSize) {
4208         case 2:
4209           ct = DM_POLYTOPE_SEGMENT;
4210           break;
4211         default:
4212           break;
4213         }
4214         break;
4215       case 2:
4216         switch (coneSize) {
4217         case 3:
4218           ct = DM_POLYTOPE_TRIANGLE;
4219           break;
4220         case 4:
4221           ct = DM_POLYTOPE_QUADRILATERAL;
4222           break;
4223         default:
4224           break;
4225         }
4226         break;
4227       case 3:
4228         switch (coneSize) {
4229         case 4:
4230           ct = DM_POLYTOPE_TETRAHEDRON;
4231           break;
4232         case 5: {
4233           const PetscInt *cone;
4234           PetscInt        faceConeSize;
4235 
4236           PetscCall(DMPlexGetCone(dm, p, &cone));
4237           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4238           switch (faceConeSize) {
4239           case 3:
4240             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4241             break;
4242           case 4:
4243             ct = DM_POLYTOPE_PYRAMID;
4244             break;
4245           }
4246         } break;
4247         case 6:
4248           ct = DM_POLYTOPE_HEXAHEDRON;
4249           break;
4250         default:
4251           break;
4252         }
4253         break;
4254       default:
4255         break;
4256       }
4257     } else if (pheight > 0) {
4258       switch (coneSize) {
4259       case 2:
4260         ct = DM_POLYTOPE_SEGMENT;
4261         break;
4262       case 3:
4263         ct = DM_POLYTOPE_TRIANGLE;
4264         break;
4265       case 4:
4266         ct = DM_POLYTOPE_QUADRILATERAL;
4267         break;
4268       default:
4269         break;
4270       }
4271     }
4272   }
4273   *pt = ct;
4274   PetscFunctionReturn(0);
4275 }
4276 
4277 /*@
4278   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4279 
4280   Collective on dm
4281 
4282   Input Parameter:
4283 . mesh - The `DMPLEX`
4284 
4285   Level: developer
4286 
4287   Note:
4288   This function is normally called automatically when a cell type is requested. It creates an
4289   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4290   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4291 
4292   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4293 
4294 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4295 @*/
4296 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4297 {
4298   DM_Plex *mesh;
4299   DMLabel  ctLabel;
4300   PetscInt pStart, pEnd, p;
4301 
4302   PetscFunctionBegin;
4303   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4304   mesh = (DM_Plex *)dm->data;
4305   PetscCall(DMCreateLabel(dm, "celltype"));
4306   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4307   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4308   for (p = pStart; p < pEnd; ++p) {
4309     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4310     PetscInt       pdepth;
4311 
4312     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4313     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4314     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4315     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4316   }
4317   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4318   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4319   PetscFunctionReturn(0);
4320 }
4321 
4322 /*@C
4323   DMPlexGetJoin - Get an array for the join of the set of points
4324 
4325   Not Collective
4326 
4327   Input Parameters:
4328 + dm - The `DMPLEX` object
4329 . numPoints - The number of input points for the join
4330 - points - The input points
4331 
4332   Output Parameters:
4333 + numCoveredPoints - The number of points in the join
4334 - coveredPoints - The points in the join
4335 
4336   Level: intermediate
4337 
4338   Note:
4339   Currently, this is restricted to a single level join
4340 
4341   Fortran Note:
4342   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4343 
4344 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4345 @*/
4346 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4347 {
4348   DM_Plex  *mesh = (DM_Plex *)dm->data;
4349   PetscInt *join[2];
4350   PetscInt  joinSize, i = 0;
4351   PetscInt  dof, off, p, c, m;
4352   PetscInt  maxSupportSize;
4353 
4354   PetscFunctionBegin;
4355   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4356   PetscValidIntPointer(points, 3);
4357   PetscValidIntPointer(numCoveredPoints, 4);
4358   PetscValidPointer(coveredPoints, 5);
4359   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4360   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4361   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4362   /* Copy in support of first point */
4363   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4364   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4365   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4366   /* Check each successive support */
4367   for (p = 1; p < numPoints; ++p) {
4368     PetscInt newJoinSize = 0;
4369 
4370     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4371     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4372     for (c = 0; c < dof; ++c) {
4373       const PetscInt point = mesh->supports[off + c];
4374 
4375       for (m = 0; m < joinSize; ++m) {
4376         if (point == join[i][m]) {
4377           join[1 - i][newJoinSize++] = point;
4378           break;
4379         }
4380       }
4381     }
4382     joinSize = newJoinSize;
4383     i        = 1 - i;
4384   }
4385   *numCoveredPoints = joinSize;
4386   *coveredPoints    = join[i];
4387   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4388   PetscFunctionReturn(0);
4389 }
4390 
4391 /*@C
4392   DMPlexRestoreJoin - Restore an array for the join of the set of points
4393 
4394   Not Collective
4395 
4396   Input Parameters:
4397 + dm - The `DMPLEX` object
4398 . numPoints - The number of input points for the join
4399 - points - The input points
4400 
4401   Output Parameters:
4402 + numCoveredPoints - The number of points in the join
4403 - coveredPoints - The points in the join
4404 
4405   Level: intermediate
4406 
4407   Fortran Note:
4408   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4409 
4410 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4411 @*/
4412 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4413 {
4414   PetscFunctionBegin;
4415   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4416   if (points) PetscValidIntPointer(points, 3);
4417   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4418   PetscValidPointer(coveredPoints, 5);
4419   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4420   if (numCoveredPoints) *numCoveredPoints = 0;
4421   PetscFunctionReturn(0);
4422 }
4423 
4424 /*@C
4425   DMPlexGetFullJoin - Get an array for the join of the set of points
4426 
4427   Not Collective
4428 
4429   Input Parameters:
4430 + dm - The `DMPLEX` object
4431 . numPoints - The number of input points for the join
4432 - points - The input points
4433 
4434   Output Parameters:
4435 + numCoveredPoints - The number of points in the join
4436 - coveredPoints - The points in the join
4437 
4438   Level: intermediate
4439 
4440   Fortran Note:
4441   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4442 
4443 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4444 @*/
4445 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4446 {
4447   PetscInt *offsets, **closures;
4448   PetscInt *join[2];
4449   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4450   PetscInt  p, d, c, m, ms;
4451 
4452   PetscFunctionBegin;
4453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4454   PetscValidIntPointer(points, 3);
4455   PetscValidIntPointer(numCoveredPoints, 4);
4456   PetscValidPointer(coveredPoints, 5);
4457 
4458   PetscCall(DMPlexGetDepth(dm, &depth));
4459   PetscCall(PetscCalloc1(numPoints, &closures));
4460   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4461   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4462   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4463   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4464   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4465 
4466   for (p = 0; p < numPoints; ++p) {
4467     PetscInt closureSize;
4468 
4469     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4470 
4471     offsets[p * (depth + 2) + 0] = 0;
4472     for (d = 0; d < depth + 1; ++d) {
4473       PetscInt pStart, pEnd, i;
4474 
4475       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4476       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4477         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4478           offsets[p * (depth + 2) + d + 1] = i;
4479           break;
4480         }
4481       }
4482       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4483     }
4484     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);
4485   }
4486   for (d = 0; d < depth + 1; ++d) {
4487     PetscInt dof;
4488 
4489     /* Copy in support of first point */
4490     dof = offsets[d + 1] - offsets[d];
4491     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4492     /* Check each successive cone */
4493     for (p = 1; p < numPoints && joinSize; ++p) {
4494       PetscInt newJoinSize = 0;
4495 
4496       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4497       for (c = 0; c < dof; ++c) {
4498         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4499 
4500         for (m = 0; m < joinSize; ++m) {
4501           if (point == join[i][m]) {
4502             join[1 - i][newJoinSize++] = point;
4503             break;
4504           }
4505         }
4506       }
4507       joinSize = newJoinSize;
4508       i        = 1 - i;
4509     }
4510     if (joinSize) break;
4511   }
4512   *numCoveredPoints = joinSize;
4513   *coveredPoints    = join[i];
4514   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4515   PetscCall(PetscFree(closures));
4516   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4517   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4518   PetscFunctionReturn(0);
4519 }
4520 
4521 /*@C
4522   DMPlexGetMeet - Get an array for the meet of the set of points
4523 
4524   Not Collective
4525 
4526   Input Parameters:
4527 + dm - The `DMPLEX` object
4528 . numPoints - The number of input points for the meet
4529 - points - The input points
4530 
4531   Output Parameters:
4532 + numCoveredPoints - The number of points in the meet
4533 - coveredPoints - The points in the meet
4534 
4535   Level: intermediate
4536 
4537   Note:
4538   Currently, this is restricted to a single level meet
4539 
4540   Fortran Notes:
4541   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4542 
4543 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4544 @*/
4545 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4546 {
4547   DM_Plex  *mesh = (DM_Plex *)dm->data;
4548   PetscInt *meet[2];
4549   PetscInt  meetSize, i = 0;
4550   PetscInt  dof, off, p, c, m;
4551   PetscInt  maxConeSize;
4552 
4553   PetscFunctionBegin;
4554   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4555   PetscValidIntPointer(points, 3);
4556   PetscValidIntPointer(numCoveringPoints, 4);
4557   PetscValidPointer(coveringPoints, 5);
4558   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4559   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4560   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4561   /* Copy in cone of first point */
4562   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4563   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4564   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4565   /* Check each successive cone */
4566   for (p = 1; p < numPoints; ++p) {
4567     PetscInt newMeetSize = 0;
4568 
4569     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4570     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4571     for (c = 0; c < dof; ++c) {
4572       const PetscInt point = mesh->cones[off + c];
4573 
4574       for (m = 0; m < meetSize; ++m) {
4575         if (point == meet[i][m]) {
4576           meet[1 - i][newMeetSize++] = point;
4577           break;
4578         }
4579       }
4580     }
4581     meetSize = newMeetSize;
4582     i        = 1 - i;
4583   }
4584   *numCoveringPoints = meetSize;
4585   *coveringPoints    = meet[i];
4586   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4587   PetscFunctionReturn(0);
4588 }
4589 
4590 /*@C
4591   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4592 
4593   Not Collective
4594 
4595   Input Parameters:
4596 + dm - The `DMPLEX` object
4597 . numPoints - The number of input points for the meet
4598 - points - The input points
4599 
4600   Output Parameters:
4601 + numCoveredPoints - The number of points in the meet
4602 - coveredPoints - The points in the meet
4603 
4604   Level: intermediate
4605 
4606   Fortran Note:
4607   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4608 
4609 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4610 @*/
4611 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4612 {
4613   PetscFunctionBegin;
4614   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4615   if (points) PetscValidIntPointer(points, 3);
4616   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4617   PetscValidPointer(coveredPoints, 5);
4618   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4619   if (numCoveredPoints) *numCoveredPoints = 0;
4620   PetscFunctionReturn(0);
4621 }
4622 
4623 /*@C
4624   DMPlexGetFullMeet - Get an array for the meet of the set of points
4625 
4626   Not Collective
4627 
4628   Input Parameters:
4629 + dm - The `DMPLEX` object
4630 . numPoints - The number of input points for the meet
4631 - points - The input points
4632 
4633   Output Parameters:
4634 + numCoveredPoints - The number of points in the meet
4635 - coveredPoints - The points in the meet
4636 
4637   Level: intermediate
4638 
4639   Fortran Note:
4640   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4641 
4642 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4643 @*/
4644 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4645 {
4646   PetscInt *offsets, **closures;
4647   PetscInt *meet[2];
4648   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4649   PetscInt  p, h, c, m, mc;
4650 
4651   PetscFunctionBegin;
4652   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4653   PetscValidIntPointer(points, 3);
4654   PetscValidIntPointer(numCoveredPoints, 4);
4655   PetscValidPointer(coveredPoints, 5);
4656 
4657   PetscCall(DMPlexGetDepth(dm, &height));
4658   PetscCall(PetscMalloc1(numPoints, &closures));
4659   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4660   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4661   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4662   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4663   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4664 
4665   for (p = 0; p < numPoints; ++p) {
4666     PetscInt closureSize;
4667 
4668     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4669 
4670     offsets[p * (height + 2) + 0] = 0;
4671     for (h = 0; h < height + 1; ++h) {
4672       PetscInt pStart, pEnd, i;
4673 
4674       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4675       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4676         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4677           offsets[p * (height + 2) + h + 1] = i;
4678           break;
4679         }
4680       }
4681       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4682     }
4683     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);
4684   }
4685   for (h = 0; h < height + 1; ++h) {
4686     PetscInt dof;
4687 
4688     /* Copy in cone of first point */
4689     dof = offsets[h + 1] - offsets[h];
4690     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4691     /* Check each successive cone */
4692     for (p = 1; p < numPoints && meetSize; ++p) {
4693       PetscInt newMeetSize = 0;
4694 
4695       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4696       for (c = 0; c < dof; ++c) {
4697         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4698 
4699         for (m = 0; m < meetSize; ++m) {
4700           if (point == meet[i][m]) {
4701             meet[1 - i][newMeetSize++] = point;
4702             break;
4703           }
4704         }
4705       }
4706       meetSize = newMeetSize;
4707       i        = 1 - i;
4708     }
4709     if (meetSize) break;
4710   }
4711   *numCoveredPoints = meetSize;
4712   *coveredPoints    = meet[i];
4713   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4714   PetscCall(PetscFree(closures));
4715   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4716   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4717   PetscFunctionReturn(0);
4718 }
4719 
4720 /*@C
4721   DMPlexEqual - Determine if two `DM` have the same topology
4722 
4723   Not Collective
4724 
4725   Input Parameters:
4726 + dmA - A `DMPLEX` object
4727 - dmB - A `DMPLEX` object
4728 
4729   Output Parameters:
4730 . equal - `PETSC_TRUE` if the topologies are identical
4731 
4732   Level: intermediate
4733 
4734   Note:
4735   We are not solving graph isomorphism, so we do not permute.
4736 
4737 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4738 @*/
4739 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4740 {
4741   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4742 
4743   PetscFunctionBegin;
4744   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4745   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4746   PetscValidBoolPointer(equal, 3);
4747 
4748   *equal = PETSC_FALSE;
4749   PetscCall(DMPlexGetDepth(dmA, &depth));
4750   PetscCall(DMPlexGetDepth(dmB, &depthB));
4751   if (depth != depthB) PetscFunctionReturn(0);
4752   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4753   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4754   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4755   for (p = pStart; p < pEnd; ++p) {
4756     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4757     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4758 
4759     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4760     PetscCall(DMPlexGetCone(dmA, p, &cone));
4761     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4762     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4763     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4764     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4765     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4766     for (c = 0; c < coneSize; ++c) {
4767       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4768       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4769     }
4770     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4771     PetscCall(DMPlexGetSupport(dmA, p, &support));
4772     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4773     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4774     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4775     for (s = 0; s < supportSize; ++s) {
4776       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4777     }
4778   }
4779   *equal = PETSC_TRUE;
4780   PetscFunctionReturn(0);
4781 }
4782 
4783 /*@C
4784   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4785 
4786   Not Collective
4787 
4788   Input Parameters:
4789 + dm         - The `DMPLEX`
4790 . cellDim    - The cell dimension
4791 - numCorners - The number of vertices on a cell
4792 
4793   Output Parameters:
4794 . numFaceVertices - The number of vertices on a face
4795 
4796   Level: developer
4797 
4798   Note:
4799   Of course this can only work for a restricted set of symmetric shapes
4800 
4801 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4802 @*/
4803 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4804 {
4805   MPI_Comm comm;
4806 
4807   PetscFunctionBegin;
4808   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4809   PetscValidIntPointer(numFaceVertices, 4);
4810   switch (cellDim) {
4811   case 0:
4812     *numFaceVertices = 0;
4813     break;
4814   case 1:
4815     *numFaceVertices = 1;
4816     break;
4817   case 2:
4818     switch (numCorners) {
4819     case 3:                 /* triangle */
4820       *numFaceVertices = 2; /* Edge has 2 vertices */
4821       break;
4822     case 4:                 /* quadrilateral */
4823       *numFaceVertices = 2; /* Edge has 2 vertices */
4824       break;
4825     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4826       *numFaceVertices = 3; /* Edge has 3 vertices */
4827       break;
4828     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4829       *numFaceVertices = 3; /* Edge has 3 vertices */
4830       break;
4831     default:
4832       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4833     }
4834     break;
4835   case 3:
4836     switch (numCorners) {
4837     case 4:                 /* tetradehdron */
4838       *numFaceVertices = 3; /* Face has 3 vertices */
4839       break;
4840     case 6:                 /* tet cohesive cells */
4841       *numFaceVertices = 4; /* Face has 4 vertices */
4842       break;
4843     case 8:                 /* hexahedron */
4844       *numFaceVertices = 4; /* Face has 4 vertices */
4845       break;
4846     case 9:                 /* tet cohesive Lagrange cells */
4847       *numFaceVertices = 6; /* Face has 6 vertices */
4848       break;
4849     case 10:                /* quadratic tetrahedron */
4850       *numFaceVertices = 6; /* Face has 6 vertices */
4851       break;
4852     case 12:                /* hex cohesive Lagrange cells */
4853       *numFaceVertices = 6; /* Face has 6 vertices */
4854       break;
4855     case 18:                /* quadratic tet cohesive Lagrange cells */
4856       *numFaceVertices = 6; /* Face has 6 vertices */
4857       break;
4858     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4859       *numFaceVertices = 9; /* Face has 9 vertices */
4860       break;
4861     default:
4862       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4863     }
4864     break;
4865   default:
4866     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4867   }
4868   PetscFunctionReturn(0);
4869 }
4870 
4871 /*@
4872   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4873 
4874   Not Collective
4875 
4876   Input Parameter:
4877 . dm    - The `DMPLEX` object
4878 
4879   Output Parameter:
4880 . depthLabel - The `DMLabel` recording point depth
4881 
4882   Level: developer
4883 
4884 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4885 @*/
4886 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4887 {
4888   PetscFunctionBegin;
4889   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4890   PetscValidPointer(depthLabel, 2);
4891   *depthLabel = dm->depthLabel;
4892   PetscFunctionReturn(0);
4893 }
4894 
4895 /*@
4896   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4897 
4898   Not Collective
4899 
4900   Input Parameter:
4901 . dm    - The `DMPLEX` object
4902 
4903   Output Parameter:
4904 . depth - The number of strata (breadth first levels) in the DAG
4905 
4906   Level: developer
4907 
4908   Notes:
4909   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4910 
4911   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4912 
4913   An empty mesh gives -1.
4914 
4915 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4916 @*/
4917 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4918 {
4919   DM_Plex *mesh = (DM_Plex *)dm->data;
4920   DMLabel  label;
4921   PetscInt d = 0;
4922 
4923   PetscFunctionBegin;
4924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4925   PetscValidIntPointer(depth, 2);
4926   if (mesh->tr) {
4927     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4928   } else {
4929     PetscCall(DMPlexGetDepthLabel(dm, &label));
4930     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4931     *depth = d - 1;
4932   }
4933   PetscFunctionReturn(0);
4934 }
4935 
4936 /*@
4937   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4938 
4939   Not Collective
4940 
4941   Input Parameters:
4942 + dm    - The `DMPLEX` object
4943 - depth - The requested depth
4944 
4945   Output Parameters:
4946 + start - The first point at this depth
4947 - end   - One beyond the last point at this depth
4948 
4949   Level: developer
4950 
4951   Notes:
4952   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4953   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
4954   higher dimension, e.g., "edges".
4955 
4956 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4957 @*/
4958 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4959 {
4960   DM_Plex *mesh = (DM_Plex *)dm->data;
4961   DMLabel  label;
4962   PetscInt pStart, pEnd;
4963 
4964   PetscFunctionBegin;
4965   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4966   if (start) {
4967     PetscValidIntPointer(start, 3);
4968     *start = 0;
4969   }
4970   if (end) {
4971     PetscValidIntPointer(end, 4);
4972     *end = 0;
4973   }
4974   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4975   if (pStart == pEnd) PetscFunctionReturn(0);
4976   if (depth < 0) {
4977     if (start) *start = pStart;
4978     if (end) *end = pEnd;
4979     PetscFunctionReturn(0);
4980   }
4981   if (mesh->tr) {
4982     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
4983   } else {
4984     PetscCall(DMPlexGetDepthLabel(dm, &label));
4985     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4986     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4987   }
4988   PetscFunctionReturn(0);
4989 }
4990 
4991 /*@
4992   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4993 
4994   Not Collective
4995 
4996   Input Parameters:
4997 + dm     - The `DMPLEX` object
4998 - height - The requested height
4999 
5000   Output Parameters:
5001 + start - The first point at this height
5002 - end   - One beyond the last point at this height
5003 
5004   Level: developer
5005 
5006   Notes:
5007   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5008   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5009   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5010 
5011 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5012 @*/
5013 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5014 {
5015   DMLabel  label;
5016   PetscInt depth, pStart, pEnd;
5017 
5018   PetscFunctionBegin;
5019   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5020   if (start) {
5021     PetscValidIntPointer(start, 3);
5022     *start = 0;
5023   }
5024   if (end) {
5025     PetscValidIntPointer(end, 4);
5026     *end = 0;
5027   }
5028   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5029   if (pStart == pEnd) PetscFunctionReturn(0);
5030   if (height < 0) {
5031     if (start) *start = pStart;
5032     if (end) *end = pEnd;
5033     PetscFunctionReturn(0);
5034   }
5035   PetscCall(DMPlexGetDepthLabel(dm, &label));
5036   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5037   PetscCall(DMLabelGetNumValues(label, &depth));
5038   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5039   PetscFunctionReturn(0);
5040 }
5041 
5042 /*@
5043   DMPlexGetPointDepth - Get the depth of a given point
5044 
5045   Not Collective
5046 
5047   Input Parameters:
5048 + dm    - The `DMPLEX` object
5049 - point - The point
5050 
5051   Output Parameter:
5052 . depth - The depth of the point
5053 
5054   Level: intermediate
5055 
5056 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5057 @*/
5058 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5059 {
5060   PetscFunctionBegin;
5061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5062   PetscValidIntPointer(depth, 3);
5063   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5064   PetscFunctionReturn(0);
5065 }
5066 
5067 /*@
5068   DMPlexGetPointHeight - Get the height of a given point
5069 
5070   Not Collective
5071 
5072   Input Parameters:
5073 + dm    - The `DMPLEX` object
5074 - point - The point
5075 
5076   Output Parameter:
5077 . height - The height of the point
5078 
5079   Level: intermediate
5080 
5081 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5082 @*/
5083 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5084 {
5085   PetscInt n, pDepth;
5086 
5087   PetscFunctionBegin;
5088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5089   PetscValidIntPointer(height, 3);
5090   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5091   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5092   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5093   PetscFunctionReturn(0);
5094 }
5095 
5096 /*@
5097   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5098 
5099   Not Collective
5100 
5101   Input Parameter:
5102 . dm - The `DMPLEX` object
5103 
5104   Output Parameter:
5105 . celltypeLabel - The `DMLabel` recording cell polytope type
5106 
5107   Level: developer
5108 
5109   Note:
5110   This function will trigger automatica computation of cell types. This can be disabled by calling
5111   `DMCreateLabel`(dm, "celltype") beforehand.
5112 
5113 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5114 @*/
5115 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5116 {
5117   PetscFunctionBegin;
5118   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5119   PetscValidPointer(celltypeLabel, 2);
5120   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5121   *celltypeLabel = dm->celltypeLabel;
5122   PetscFunctionReturn(0);
5123 }
5124 
5125 /*@
5126   DMPlexGetCellType - Get the polytope type of a given cell
5127 
5128   Not Collective
5129 
5130   Input Parameters:
5131 + dm   - The `DMPLEX` object
5132 - cell - The cell
5133 
5134   Output Parameter:
5135 . celltype - The polytope type of the cell
5136 
5137   Level: intermediate
5138 
5139 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5140 @*/
5141 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5142 {
5143   DM_Plex *mesh = (DM_Plex *)dm->data;
5144   DMLabel  label;
5145   PetscInt ct;
5146 
5147   PetscFunctionBegin;
5148   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5149   PetscValidPointer(celltype, 3);
5150   if (mesh->tr) {
5151     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5152   } else {
5153     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5154     PetscCall(DMLabelGetValue(label, cell, &ct));
5155     PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5156     *celltype = (DMPolytopeType)ct;
5157   }
5158   PetscFunctionReturn(0);
5159 }
5160 
5161 /*@
5162   DMPlexSetCellType - Set the polytope type of a given cell
5163 
5164   Not Collective
5165 
5166   Input Parameters:
5167 + dm   - The `DMPLEX` object
5168 . cell - The cell
5169 - celltype - The polytope type of the cell
5170 
5171   Level: advanced
5172 
5173   Note:
5174   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5175   is executed. This function will override the computed type. However, if automatic classification will not succeed
5176   and a user wants to manually specify all types, the classification must be disabled by calling
5177   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5178 
5179 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5180 @*/
5181 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5182 {
5183   DMLabel label;
5184 
5185   PetscFunctionBegin;
5186   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5187   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5188   PetscCall(DMLabelSetValue(label, cell, celltype));
5189   PetscFunctionReturn(0);
5190 }
5191 
5192 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5193 {
5194   PetscSection section, s;
5195   Mat          m;
5196   PetscInt     maxHeight;
5197   const char  *prefix;
5198 
5199   PetscFunctionBegin;
5200   PetscCall(DMClone(dm, cdm));
5201   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5202   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5203   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5204   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5205   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5206   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5207   PetscCall(DMSetLocalSection(*cdm, section));
5208   PetscCall(PetscSectionDestroy(&section));
5209   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5210   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5211   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5212   PetscCall(PetscSectionDestroy(&s));
5213   PetscCall(MatDestroy(&m));
5214 
5215   PetscCall(DMSetNumFields(*cdm, 1));
5216   PetscCall(DMCreateDS(*cdm));
5217   (*cdm)->cloneOpts = PETSC_TRUE;
5218   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5219   PetscFunctionReturn(0);
5220 }
5221 
5222 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5223 {
5224   Vec coordsLocal, cellCoordsLocal;
5225   DM  coordsDM, cellCoordsDM;
5226 
5227   PetscFunctionBegin;
5228   *field = NULL;
5229   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5230   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5231   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5232   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5233   if (coordsLocal && coordsDM) {
5234     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5235     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5236   }
5237   PetscFunctionReturn(0);
5238 }
5239 
5240 /*@C
5241   DMPlexGetConeSection - Return a section which describes the layout of cone data
5242 
5243   Not Collective
5244 
5245   Input Parameters:
5246 . dm        - The `DMPLEX` object
5247 
5248   Output Parameter:
5249 . section - The `PetscSection` object
5250 
5251   Level: developer
5252 
5253 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5254 @*/
5255 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5256 {
5257   DM_Plex *mesh = (DM_Plex *)dm->data;
5258 
5259   PetscFunctionBegin;
5260   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5261   if (section) *section = mesh->coneSection;
5262   PetscFunctionReturn(0);
5263 }
5264 
5265 /*@C
5266   DMPlexGetSupportSection - Return a section which describes the layout of support data
5267 
5268   Not Collective
5269 
5270   Input Parameters:
5271 . dm        - The `DMPLEX` object
5272 
5273   Output Parameter:
5274 . section - The `PetscSection` object
5275 
5276   Level: developer
5277 
5278 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5279 @*/
5280 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5281 {
5282   DM_Plex *mesh = (DM_Plex *)dm->data;
5283 
5284   PetscFunctionBegin;
5285   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5286   if (section) *section = mesh->supportSection;
5287   PetscFunctionReturn(0);
5288 }
5289 
5290 /*@C
5291   DMPlexGetCones - Return cone data
5292 
5293   Not Collective
5294 
5295   Input Parameters:
5296 . dm        - The `DMPLEX` object
5297 
5298   Output Parameter:
5299 . cones - The cone for each point
5300 
5301   Level: developer
5302 
5303 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5304 @*/
5305 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5306 {
5307   DM_Plex *mesh = (DM_Plex *)dm->data;
5308 
5309   PetscFunctionBegin;
5310   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5311   if (cones) *cones = mesh->cones;
5312   PetscFunctionReturn(0);
5313 }
5314 
5315 /*@C
5316   DMPlexGetConeOrientations - Return cone orientation data
5317 
5318   Not Collective
5319 
5320   Input Parameters:
5321 . dm        - The `DMPLEX` object
5322 
5323   Output Parameter:
5324 . coneOrientations - The array of cone orientations for all points
5325 
5326   Level: developer
5327 
5328   Notes:
5329   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5330 
5331   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5332 
5333 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5334 @*/
5335 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5336 {
5337   DM_Plex *mesh = (DM_Plex *)dm->data;
5338 
5339   PetscFunctionBegin;
5340   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5341   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5342   PetscFunctionReturn(0);
5343 }
5344 
5345 /******************************** FEM Support **********************************/
5346 
5347 /*
5348  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5349  representing a line in the section.
5350 */
5351 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5352 {
5353   PetscFunctionBeginHot;
5354   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5355   if (line < 0) {
5356     *k  = 0;
5357     *Nc = 0;
5358   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5359     *k = 1;
5360   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5361     /* An order k SEM disc has k-1 dofs on an edge */
5362     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5363     *k = *k / *Nc + 1;
5364   }
5365   PetscFunctionReturn(0);
5366 }
5367 
5368 /*@
5369 
5370   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5371   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5372   section provided (or the section of the DM).
5373 
5374   Input Parameters:
5375 + dm      - The DM
5376 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5377 - section - The PetscSection to reorder, or NULL for the default section
5378 
5379   Example:
5380   A typical interpolated single-quad mesh might order points as
5381 .vb
5382   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5383 
5384   v4 -- e6 -- v3
5385   |           |
5386   e7    c0    e8
5387   |           |
5388   v1 -- e5 -- v2
5389 .ve
5390 
5391   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5392   dofs in the order of points, e.g.,
5393 .vb
5394     c0 -> [0,1,2,3]
5395     v1 -> [4]
5396     ...
5397     e5 -> [8, 9]
5398 .ve
5399 
5400   which corresponds to the dofs
5401 .vb
5402     6   10  11  7
5403     13  2   3   15
5404     12  0   1   14
5405     4   8   9   5
5406 .ve
5407 
5408   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5409 .vb
5410   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5411 .ve
5412 
5413   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5414 .vb
5415    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5416 .ve
5417 
5418   Level: developer
5419 
5420   Note:
5421   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5422   degree of the basis.
5423 
5424 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5425 @*/
5426 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5427 {
5428   DMLabel   label;
5429   PetscInt  dim, depth = -1, eStart = -1, Nf;
5430   PetscBool vertexchart;
5431 
5432   PetscFunctionBegin;
5433   PetscCall(DMGetDimension(dm, &dim));
5434   if (dim < 1) PetscFunctionReturn(0);
5435   if (point < 0) {
5436     PetscInt sStart, sEnd;
5437 
5438     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5439     point = sEnd - sStart ? sStart : point;
5440   }
5441   PetscCall(DMPlexGetDepthLabel(dm, &label));
5442   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5443   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5444   if (depth == 1) {
5445     eStart = point;
5446   } else if (depth == dim) {
5447     const PetscInt *cone;
5448 
5449     PetscCall(DMPlexGetCone(dm, point, &cone));
5450     if (dim == 2) eStart = cone[0];
5451     else if (dim == 3) {
5452       const PetscInt *cone2;
5453       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5454       eStart = cone2[0];
5455     } 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);
5456   } 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);
5457   { /* Determine whether the chart covers all points or just vertices. */
5458     PetscInt pStart, pEnd, cStart, cEnd;
5459     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5460     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5461     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5462     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5463     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5464   }
5465   PetscCall(PetscSectionGetNumFields(section, &Nf));
5466   for (PetscInt d = 1; d <= dim; d++) {
5467     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5468     PetscInt *perm;
5469 
5470     for (f = 0; f < Nf; ++f) {
5471       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5472       size += PetscPowInt(k + 1, d) * Nc;
5473     }
5474     PetscCall(PetscMalloc1(size, &perm));
5475     for (f = 0; f < Nf; ++f) {
5476       switch (d) {
5477       case 1:
5478         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5479         /*
5480          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5481          We want              [ vtx0; edge of length k-1; vtx1 ]
5482          */
5483         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5484         for (i = 0; i < k - 1; i++)
5485           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5486         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5487         foffset = offset;
5488         break;
5489       case 2:
5490         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5491         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5492         /* The SEM order is
5493 
5494          v_lb, {e_b}, v_rb,
5495          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5496          v_lt, reverse {e_t}, v_rt
5497          */
5498         {
5499           const PetscInt of   = 0;
5500           const PetscInt oeb  = of + PetscSqr(k - 1);
5501           const PetscInt oer  = oeb + (k - 1);
5502           const PetscInt oet  = oer + (k - 1);
5503           const PetscInt oel  = oet + (k - 1);
5504           const PetscInt ovlb = oel + (k - 1);
5505           const PetscInt ovrb = ovlb + 1;
5506           const PetscInt ovrt = ovrb + 1;
5507           const PetscInt ovlt = ovrt + 1;
5508           PetscInt       o;
5509 
5510           /* bottom */
5511           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5512           for (o = oeb; o < oer; ++o)
5513             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5514           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5515           /* middle */
5516           for (i = 0; i < k - 1; ++i) {
5517             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5518             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5519               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5520             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5521           }
5522           /* top */
5523           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5524           for (o = oel - 1; o >= oet; --o)
5525             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5526           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5527           foffset = offset;
5528         }
5529         break;
5530       case 3:
5531         /* The original hex closure is
5532 
5533          {c,
5534          f_b, f_t, f_f, f_b, f_r, f_l,
5535          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5536          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5537          */
5538         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5539         /* The SEM order is
5540          Bottom Slice
5541          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5542          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5543          v_blb, {e_bb}, v_brb,
5544 
5545          Middle Slice (j)
5546          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5547          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5548          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5549 
5550          Top Slice
5551          v_tlf, {e_tf}, v_trf,
5552          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5553          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5554          */
5555         {
5556           const PetscInt oc    = 0;
5557           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5558           const PetscInt oft   = ofb + PetscSqr(k - 1);
5559           const PetscInt off   = oft + PetscSqr(k - 1);
5560           const PetscInt ofk   = off + PetscSqr(k - 1);
5561           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5562           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5563           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5564           const PetscInt oebb  = oebl + (k - 1);
5565           const PetscInt oebr  = oebb + (k - 1);
5566           const PetscInt oebf  = oebr + (k - 1);
5567           const PetscInt oetf  = oebf + (k - 1);
5568           const PetscInt oetr  = oetf + (k - 1);
5569           const PetscInt oetb  = oetr + (k - 1);
5570           const PetscInt oetl  = oetb + (k - 1);
5571           const PetscInt oerf  = oetl + (k - 1);
5572           const PetscInt oelf  = oerf + (k - 1);
5573           const PetscInt oelb  = oelf + (k - 1);
5574           const PetscInt oerb  = oelb + (k - 1);
5575           const PetscInt ovblf = oerb + (k - 1);
5576           const PetscInt ovblb = ovblf + 1;
5577           const PetscInt ovbrb = ovblb + 1;
5578           const PetscInt ovbrf = ovbrb + 1;
5579           const PetscInt ovtlf = ovbrf + 1;
5580           const PetscInt ovtrf = ovtlf + 1;
5581           const PetscInt ovtrb = ovtrf + 1;
5582           const PetscInt ovtlb = ovtrb + 1;
5583           PetscInt       o, n;
5584 
5585           /* Bottom Slice */
5586           /*   bottom */
5587           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5588           for (o = oetf - 1; o >= oebf; --o)
5589             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5590           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5591           /*   middle */
5592           for (i = 0; i < k - 1; ++i) {
5593             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5594             for (n = 0; n < k - 1; ++n) {
5595               o = ofb + n * (k - 1) + i;
5596               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5597             }
5598             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5599           }
5600           /*   top */
5601           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5602           for (o = oebb; o < oebr; ++o)
5603             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5604           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5605 
5606           /* Middle Slice */
5607           for (j = 0; j < k - 1; ++j) {
5608             /*   bottom */
5609             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5610             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5611               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5612             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5613             /*   middle */
5614             for (i = 0; i < k - 1; ++i) {
5615               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5616               for (n = 0; n < k - 1; ++n)
5617                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5618               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5619             }
5620             /*   top */
5621             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5622             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5623               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5624             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5625           }
5626 
5627           /* Top Slice */
5628           /*   bottom */
5629           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5630           for (o = oetf; o < oetr; ++o)
5631             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5632           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5633           /*   middle */
5634           for (i = 0; i < k - 1; ++i) {
5635             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5636             for (n = 0; n < k - 1; ++n)
5637               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5638             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5639           }
5640           /*   top */
5641           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5642           for (o = oetl - 1; o >= oetb; --o)
5643             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5644           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5645 
5646           foffset = offset;
5647         }
5648         break;
5649       default:
5650         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5651       }
5652     }
5653     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5654     /* Check permutation */
5655     {
5656       PetscInt *check;
5657 
5658       PetscCall(PetscMalloc1(size, &check));
5659       for (i = 0; i < size; ++i) {
5660         check[i] = -1;
5661         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5662       }
5663       for (i = 0; i < size; ++i) check[perm[i]] = i;
5664       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5665       PetscCall(PetscFree(check));
5666     }
5667     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5668     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5669       PetscInt *loc_perm;
5670       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5671       for (PetscInt i = 0; i < size; i++) {
5672         loc_perm[i]        = perm[i];
5673         loc_perm[size + i] = size + perm[i];
5674       }
5675       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5676     }
5677   }
5678   PetscFunctionReturn(0);
5679 }
5680 
5681 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5682 {
5683   PetscDS  prob;
5684   PetscInt depth, Nf, h;
5685   DMLabel  label;
5686 
5687   PetscFunctionBeginHot;
5688   PetscCall(DMGetDS(dm, &prob));
5689   Nf      = prob->Nf;
5690   label   = dm->depthLabel;
5691   *dspace = NULL;
5692   if (field < Nf) {
5693     PetscObject disc = prob->disc[field];
5694 
5695     if (disc->classid == PETSCFE_CLASSID) {
5696       PetscDualSpace dsp;
5697 
5698       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5699       PetscCall(DMLabelGetNumValues(label, &depth));
5700       PetscCall(DMLabelGetValue(label, point, &h));
5701       h = depth - 1 - h;
5702       if (h) {
5703         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5704       } else {
5705         *dspace = dsp;
5706       }
5707     }
5708   }
5709   PetscFunctionReturn(0);
5710 }
5711 
5712 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5713 {
5714   PetscScalar       *array;
5715   const PetscScalar *vArray;
5716   const PetscInt    *cone, *coneO;
5717   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5718 
5719   PetscFunctionBeginHot;
5720   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5721   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5722   PetscCall(DMPlexGetCone(dm, point, &cone));
5723   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5724   if (!values || !*values) {
5725     if ((point >= pStart) && (point < pEnd)) {
5726       PetscInt dof;
5727 
5728       PetscCall(PetscSectionGetDof(section, point, &dof));
5729       size += dof;
5730     }
5731     for (p = 0; p < numPoints; ++p) {
5732       const PetscInt cp = cone[p];
5733       PetscInt       dof;
5734 
5735       if ((cp < pStart) || (cp >= pEnd)) continue;
5736       PetscCall(PetscSectionGetDof(section, cp, &dof));
5737       size += dof;
5738     }
5739     if (!values) {
5740       if (csize) *csize = size;
5741       PetscFunctionReturn(0);
5742     }
5743     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5744   } else {
5745     array = *values;
5746   }
5747   size = 0;
5748   PetscCall(VecGetArrayRead(v, &vArray));
5749   if ((point >= pStart) && (point < pEnd)) {
5750     PetscInt           dof, off, d;
5751     const PetscScalar *varr;
5752 
5753     PetscCall(PetscSectionGetDof(section, point, &dof));
5754     PetscCall(PetscSectionGetOffset(section, point, &off));
5755     varr = &vArray[off];
5756     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5757     size += dof;
5758   }
5759   for (p = 0; p < numPoints; ++p) {
5760     const PetscInt     cp = cone[p];
5761     PetscInt           o  = coneO[p];
5762     PetscInt           dof, off, d;
5763     const PetscScalar *varr;
5764 
5765     if ((cp < pStart) || (cp >= pEnd)) continue;
5766     PetscCall(PetscSectionGetDof(section, cp, &dof));
5767     PetscCall(PetscSectionGetOffset(section, cp, &off));
5768     varr = &vArray[off];
5769     if (o >= 0) {
5770       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5771     } else {
5772       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5773     }
5774     size += dof;
5775   }
5776   PetscCall(VecRestoreArrayRead(v, &vArray));
5777   if (!*values) {
5778     if (csize) *csize = size;
5779     *values = array;
5780   } else {
5781     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5782     *csize = size;
5783   }
5784   PetscFunctionReturn(0);
5785 }
5786 
5787 /* Compress out points not in the section */
5788 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5789 {
5790   const PetscInt np = *numPoints;
5791   PetscInt       pStart, pEnd, p, q;
5792 
5793   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5794   for (p = 0, q = 0; p < np; ++p) {
5795     const PetscInt r = points[p * 2];
5796     if ((r >= pStart) && (r < pEnd)) {
5797       points[q * 2]     = r;
5798       points[q * 2 + 1] = points[p * 2 + 1];
5799       ++q;
5800     }
5801   }
5802   *numPoints = q;
5803   return 0;
5804 }
5805 
5806 /* Compressed closure does not apply closure permutation */
5807 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5808 {
5809   const PetscInt *cla = NULL;
5810   PetscInt        np, *pts = NULL;
5811 
5812   PetscFunctionBeginHot;
5813   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5814   if (*clPoints) {
5815     PetscInt dof, off;
5816 
5817     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5818     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5819     PetscCall(ISGetIndices(*clPoints, &cla));
5820     np  = dof / 2;
5821     pts = (PetscInt *)&cla[off];
5822   } else {
5823     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5824     PetscCall(CompressPoints_Private(section, &np, pts));
5825   }
5826   *numPoints = np;
5827   *points    = pts;
5828   *clp       = cla;
5829   PetscFunctionReturn(0);
5830 }
5831 
5832 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5833 {
5834   PetscFunctionBeginHot;
5835   if (!*clPoints) {
5836     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5837   } else {
5838     PetscCall(ISRestoreIndices(*clPoints, clp));
5839   }
5840   *numPoints = 0;
5841   *points    = NULL;
5842   *clSec     = NULL;
5843   *clPoints  = NULL;
5844   *clp       = NULL;
5845   PetscFunctionReturn(0);
5846 }
5847 
5848 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5849 {
5850   PetscInt            offset = 0, p;
5851   const PetscInt    **perms  = NULL;
5852   const PetscScalar **flips  = NULL;
5853 
5854   PetscFunctionBeginHot;
5855   *size = 0;
5856   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5857   for (p = 0; p < numPoints; p++) {
5858     const PetscInt     point = points[2 * p];
5859     const PetscInt    *perm  = perms ? perms[p] : NULL;
5860     const PetscScalar *flip  = flips ? flips[p] : NULL;
5861     PetscInt           dof, off, d;
5862     const PetscScalar *varr;
5863 
5864     PetscCall(PetscSectionGetDof(section, point, &dof));
5865     PetscCall(PetscSectionGetOffset(section, point, &off));
5866     varr = &vArray[off];
5867     if (clperm) {
5868       if (perm) {
5869         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5870       } else {
5871         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5872       }
5873       if (flip) {
5874         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5875       }
5876     } else {
5877       if (perm) {
5878         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5879       } else {
5880         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5881       }
5882       if (flip) {
5883         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5884       }
5885     }
5886     offset += dof;
5887   }
5888   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5889   *size = offset;
5890   PetscFunctionReturn(0);
5891 }
5892 
5893 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[])
5894 {
5895   PetscInt offset = 0, f;
5896 
5897   PetscFunctionBeginHot;
5898   *size = 0;
5899   for (f = 0; f < numFields; ++f) {
5900     PetscInt            p;
5901     const PetscInt    **perms = NULL;
5902     const PetscScalar **flips = NULL;
5903 
5904     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5905     for (p = 0; p < numPoints; p++) {
5906       const PetscInt     point = points[2 * p];
5907       PetscInt           fdof, foff, b;
5908       const PetscScalar *varr;
5909       const PetscInt    *perm = perms ? perms[p] : NULL;
5910       const PetscScalar *flip = flips ? flips[p] : NULL;
5911 
5912       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5913       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5914       varr = &vArray[foff];
5915       if (clperm) {
5916         if (perm) {
5917           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5918         } else {
5919           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5920         }
5921         if (flip) {
5922           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5923         }
5924       } else {
5925         if (perm) {
5926           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5927         } else {
5928           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5929         }
5930         if (flip) {
5931           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5932         }
5933       }
5934       offset += fdof;
5935     }
5936     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5937   }
5938   *size = offset;
5939   PetscFunctionReturn(0);
5940 }
5941 
5942 /*@C
5943   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5944 
5945   Not collective
5946 
5947   Input Parameters:
5948 + dm - The `DM`
5949 . section - The section describing the layout in v, or NULL to use the default section
5950 . v - The local vector
5951 - point - The point in the `DM`
5952 
5953   Input/Output Parameters:
5954 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5955 - values - An array to use for the values, or NULL to have it allocated automatically;
5956            if the user provided NULL, it is a borrowed array and should not be freed
5957 
5958   Level: intermediate
5959 
5960   Notes:
5961   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to NULL in the
5962   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
5963   assembly function, and a user may already have allocated storage for this operation.
5964 
5965   A typical use could be
5966 .vb
5967    values = NULL;
5968    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5969    for (cl = 0; cl < clSize; ++cl) {
5970      <Compute on closure>
5971    }
5972    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5973 .ve
5974   or
5975 .vb
5976    PetscMalloc1(clMaxSize, &values);
5977    for (p = pStart; p < pEnd; ++p) {
5978      clSize = clMaxSize;
5979      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5980      for (cl = 0; cl < clSize; ++cl) {
5981        <Compute on closure>
5982      }
5983    }
5984    PetscFree(values);
5985 .ve
5986 
5987   Fortran Note:
5988   The csize argument is not present in the Fortran binding since it is internal to the array.
5989 
5990 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5991 @*/
5992 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5993 {
5994   PetscSection    clSection;
5995   IS              clPoints;
5996   PetscInt       *points = NULL;
5997   const PetscInt *clp, *perm;
5998   PetscInt        depth, numFields, numPoints, asize;
5999 
6000   PetscFunctionBeginHot;
6001   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6002   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6003   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6004   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6005   PetscCall(DMPlexGetDepth(dm, &depth));
6006   PetscCall(PetscSectionGetNumFields(section, &numFields));
6007   if (depth == 1 && numFields < 2) {
6008     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6009     PetscFunctionReturn(0);
6010   }
6011   /* Get points */
6012   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6013   /* Get sizes */
6014   asize = 0;
6015   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6016     PetscInt dof;
6017     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6018     asize += dof;
6019   }
6020   if (values) {
6021     const PetscScalar *vArray;
6022     PetscInt           size;
6023 
6024     if (*values) {
6025       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);
6026     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6027     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6028     PetscCall(VecGetArrayRead(v, &vArray));
6029     /* Get values */
6030     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6031     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6032     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6033     /* Cleanup array */
6034     PetscCall(VecRestoreArrayRead(v, &vArray));
6035   }
6036   if (csize) *csize = asize;
6037   /* Cleanup points */
6038   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6039   PetscFunctionReturn(0);
6040 }
6041 
6042 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6043 {
6044   DMLabel            depthLabel;
6045   PetscSection       clSection;
6046   IS                 clPoints;
6047   PetscScalar       *array;
6048   const PetscScalar *vArray;
6049   PetscInt          *points = NULL;
6050   const PetscInt    *clp, *perm = NULL;
6051   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6052 
6053   PetscFunctionBeginHot;
6054   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6055   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6056   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6057   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6058   PetscCall(DMPlexGetDepth(dm, &mdepth));
6059   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6060   PetscCall(PetscSectionGetNumFields(section, &numFields));
6061   if (mdepth == 1 && numFields < 2) {
6062     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6063     PetscFunctionReturn(0);
6064   }
6065   /* Get points */
6066   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6067   for (clsize = 0, p = 0; p < Np; p++) {
6068     PetscInt dof;
6069     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6070     clsize += dof;
6071   }
6072   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6073   /* Filter points */
6074   for (p = 0; p < numPoints * 2; p += 2) {
6075     PetscInt dep;
6076 
6077     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6078     if (dep != depth) continue;
6079     points[Np * 2 + 0] = points[p];
6080     points[Np * 2 + 1] = points[p + 1];
6081     ++Np;
6082   }
6083   /* Get array */
6084   if (!values || !*values) {
6085     PetscInt asize = 0, dof;
6086 
6087     for (p = 0; p < Np * 2; p += 2) {
6088       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6089       asize += dof;
6090     }
6091     if (!values) {
6092       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6093       if (csize) *csize = asize;
6094       PetscFunctionReturn(0);
6095     }
6096     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6097   } else {
6098     array = *values;
6099   }
6100   PetscCall(VecGetArrayRead(v, &vArray));
6101   /* Get values */
6102   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6103   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6104   /* Cleanup points */
6105   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6106   /* Cleanup array */
6107   PetscCall(VecRestoreArrayRead(v, &vArray));
6108   if (!*values) {
6109     if (csize) *csize = size;
6110     *values = array;
6111   } else {
6112     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6113     *csize = size;
6114   }
6115   PetscFunctionReturn(0);
6116 }
6117 
6118 /*@C
6119   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6120 
6121   Not collective
6122 
6123   Input Parameters:
6124 + dm - The `DM`
6125 . section - The section describing the layout in v, or NULL to use the default section
6126 . v - The local vector
6127 . point - The point in the `DM`
6128 . csize - The number of values in the closure, or NULL
6129 - values - The array of values, which is a borrowed array and should not be freed
6130 
6131   Level: intermediate
6132 
6133   Note:
6134   The array values are discarded and not copied back into v. In order to copy values back to v, use `DMPlexVecSetClosure()`
6135 
6136   Fortran Note:
6137   The csize argument is not present in the Fortran binding since it is internal to the array.
6138 
6139 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6140 @*/
6141 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6142 {
6143   PetscInt size = 0;
6144 
6145   PetscFunctionBegin;
6146   /* Should work without recalculating size */
6147   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6148   *values = NULL;
6149   PetscFunctionReturn(0);
6150 }
6151 
6152 static inline void add(PetscScalar *x, PetscScalar y)
6153 {
6154   *x += y;
6155 }
6156 static inline void insert(PetscScalar *x, PetscScalar y)
6157 {
6158   *x = y;
6159 }
6160 
6161 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[])
6162 {
6163   PetscInt        cdof;  /* The number of constraints on this point */
6164   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6165   PetscScalar    *a;
6166   PetscInt        off, cind = 0, k;
6167 
6168   PetscFunctionBegin;
6169   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6170   PetscCall(PetscSectionGetOffset(section, point, &off));
6171   a = &array[off];
6172   if (!cdof || setBC) {
6173     if (clperm) {
6174       if (perm) {
6175         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6176       } else {
6177         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6178       }
6179     } else {
6180       if (perm) {
6181         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6182       } else {
6183         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6184       }
6185     }
6186   } else {
6187     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6188     if (clperm) {
6189       if (perm) {
6190         for (k = 0; k < dof; ++k) {
6191           if ((cind < cdof) && (k == cdofs[cind])) {
6192             ++cind;
6193             continue;
6194           }
6195           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6196         }
6197       } else {
6198         for (k = 0; k < dof; ++k) {
6199           if ((cind < cdof) && (k == cdofs[cind])) {
6200             ++cind;
6201             continue;
6202           }
6203           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6204         }
6205       }
6206     } else {
6207       if (perm) {
6208         for (k = 0; k < dof; ++k) {
6209           if ((cind < cdof) && (k == cdofs[cind])) {
6210             ++cind;
6211             continue;
6212           }
6213           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6214         }
6215       } else {
6216         for (k = 0; k < dof; ++k) {
6217           if ((cind < cdof) && (k == cdofs[cind])) {
6218             ++cind;
6219             continue;
6220           }
6221           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6222         }
6223       }
6224     }
6225   }
6226   PetscFunctionReturn(0);
6227 }
6228 
6229 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[])
6230 {
6231   PetscInt        cdof;  /* The number of constraints on this point */
6232   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6233   PetscScalar    *a;
6234   PetscInt        off, cind = 0, k;
6235 
6236   PetscFunctionBegin;
6237   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6238   PetscCall(PetscSectionGetOffset(section, point, &off));
6239   a = &array[off];
6240   if (cdof) {
6241     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6242     if (clperm) {
6243       if (perm) {
6244         for (k = 0; k < dof; ++k) {
6245           if ((cind < cdof) && (k == cdofs[cind])) {
6246             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6247             cind++;
6248           }
6249         }
6250       } else {
6251         for (k = 0; k < dof; ++k) {
6252           if ((cind < cdof) && (k == cdofs[cind])) {
6253             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6254             cind++;
6255           }
6256         }
6257       }
6258     } else {
6259       if (perm) {
6260         for (k = 0; k < dof; ++k) {
6261           if ((cind < cdof) && (k == cdofs[cind])) {
6262             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6263             cind++;
6264           }
6265         }
6266       } else {
6267         for (k = 0; k < dof; ++k) {
6268           if ((cind < cdof) && (k == cdofs[cind])) {
6269             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6270             cind++;
6271           }
6272         }
6273       }
6274     }
6275   }
6276   PetscFunctionReturn(0);
6277 }
6278 
6279 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[])
6280 {
6281   PetscScalar    *a;
6282   PetscInt        fdof, foff, fcdof, foffset = *offset;
6283   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6284   PetscInt        cind = 0, b;
6285 
6286   PetscFunctionBegin;
6287   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6288   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6289   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6290   a = &array[foff];
6291   if (!fcdof || setBC) {
6292     if (clperm) {
6293       if (perm) {
6294         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6295       } else {
6296         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6297       }
6298     } else {
6299       if (perm) {
6300         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6301       } else {
6302         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6303       }
6304     }
6305   } else {
6306     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6307     if (clperm) {
6308       if (perm) {
6309         for (b = 0; b < fdof; b++) {
6310           if ((cind < fcdof) && (b == fcdofs[cind])) {
6311             ++cind;
6312             continue;
6313           }
6314           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6315         }
6316       } else {
6317         for (b = 0; b < fdof; b++) {
6318           if ((cind < fcdof) && (b == fcdofs[cind])) {
6319             ++cind;
6320             continue;
6321           }
6322           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6323         }
6324       }
6325     } else {
6326       if (perm) {
6327         for (b = 0; b < fdof; b++) {
6328           if ((cind < fcdof) && (b == fcdofs[cind])) {
6329             ++cind;
6330             continue;
6331           }
6332           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6333         }
6334       } else {
6335         for (b = 0; b < fdof; b++) {
6336           if ((cind < fcdof) && (b == fcdofs[cind])) {
6337             ++cind;
6338             continue;
6339           }
6340           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6341         }
6342       }
6343     }
6344   }
6345   *offset += fdof;
6346   PetscFunctionReturn(0);
6347 }
6348 
6349 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[])
6350 {
6351   PetscScalar    *a;
6352   PetscInt        fdof, foff, fcdof, foffset = *offset;
6353   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6354   PetscInt        Nc, cind = 0, ncind = 0, b;
6355   PetscBool       ncSet, fcSet;
6356 
6357   PetscFunctionBegin;
6358   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6359   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6360   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6361   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6362   a = &array[foff];
6363   if (fcdof) {
6364     /* We just override fcdof and fcdofs with Ncc and comps */
6365     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6366     if (clperm) {
6367       if (perm) {
6368         if (comps) {
6369           for (b = 0; b < fdof; b++) {
6370             ncSet = fcSet = PETSC_FALSE;
6371             if (b % Nc == comps[ncind]) {
6372               ncind = (ncind + 1) % Ncc;
6373               ncSet = PETSC_TRUE;
6374             }
6375             if ((cind < fcdof) && (b == fcdofs[cind])) {
6376               ++cind;
6377               fcSet = PETSC_TRUE;
6378             }
6379             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6380           }
6381         } else {
6382           for (b = 0; b < fdof; b++) {
6383             if ((cind < fcdof) && (b == fcdofs[cind])) {
6384               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6385               ++cind;
6386             }
6387           }
6388         }
6389       } else {
6390         if (comps) {
6391           for (b = 0; b < fdof; b++) {
6392             ncSet = fcSet = PETSC_FALSE;
6393             if (b % Nc == comps[ncind]) {
6394               ncind = (ncind + 1) % Ncc;
6395               ncSet = PETSC_TRUE;
6396             }
6397             if ((cind < fcdof) && (b == fcdofs[cind])) {
6398               ++cind;
6399               fcSet = PETSC_TRUE;
6400             }
6401             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6402           }
6403         } else {
6404           for (b = 0; b < fdof; b++) {
6405             if ((cind < fcdof) && (b == fcdofs[cind])) {
6406               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6407               ++cind;
6408             }
6409           }
6410         }
6411       }
6412     } else {
6413       if (perm) {
6414         if (comps) {
6415           for (b = 0; b < fdof; b++) {
6416             ncSet = fcSet = PETSC_FALSE;
6417             if (b % Nc == comps[ncind]) {
6418               ncind = (ncind + 1) % Ncc;
6419               ncSet = PETSC_TRUE;
6420             }
6421             if ((cind < fcdof) && (b == fcdofs[cind])) {
6422               ++cind;
6423               fcSet = PETSC_TRUE;
6424             }
6425             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6426           }
6427         } else {
6428           for (b = 0; b < fdof; b++) {
6429             if ((cind < fcdof) && (b == fcdofs[cind])) {
6430               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6431               ++cind;
6432             }
6433           }
6434         }
6435       } else {
6436         if (comps) {
6437           for (b = 0; b < fdof; b++) {
6438             ncSet = fcSet = PETSC_FALSE;
6439             if (b % Nc == comps[ncind]) {
6440               ncind = (ncind + 1) % Ncc;
6441               ncSet = PETSC_TRUE;
6442             }
6443             if ((cind < fcdof) && (b == fcdofs[cind])) {
6444               ++cind;
6445               fcSet = PETSC_TRUE;
6446             }
6447             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6448           }
6449         } else {
6450           for (b = 0; b < fdof; b++) {
6451             if ((cind < fcdof) && (b == fcdofs[cind])) {
6452               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6453               ++cind;
6454             }
6455           }
6456         }
6457       }
6458     }
6459   }
6460   *offset += fdof;
6461   PetscFunctionReturn(0);
6462 }
6463 
6464 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6465 {
6466   PetscScalar    *array;
6467   const PetscInt *cone, *coneO;
6468   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6469 
6470   PetscFunctionBeginHot;
6471   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6472   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6473   PetscCall(DMPlexGetCone(dm, point, &cone));
6474   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6475   PetscCall(VecGetArray(v, &array));
6476   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6477     const PetscInt cp = !p ? point : cone[p - 1];
6478     const PetscInt o  = !p ? 0 : coneO[p - 1];
6479 
6480     if ((cp < pStart) || (cp >= pEnd)) {
6481       dof = 0;
6482       continue;
6483     }
6484     PetscCall(PetscSectionGetDof(section, cp, &dof));
6485     /* ADD_VALUES */
6486     {
6487       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6488       PetscScalar    *a;
6489       PetscInt        cdof, coff, cind = 0, k;
6490 
6491       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6492       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6493       a = &array[coff];
6494       if (!cdof) {
6495         if (o >= 0) {
6496           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6497         } else {
6498           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6499         }
6500       } else {
6501         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6502         if (o >= 0) {
6503           for (k = 0; k < dof; ++k) {
6504             if ((cind < cdof) && (k == cdofs[cind])) {
6505               ++cind;
6506               continue;
6507             }
6508             a[k] += values[off + k];
6509           }
6510         } else {
6511           for (k = 0; k < dof; ++k) {
6512             if ((cind < cdof) && (k == cdofs[cind])) {
6513               ++cind;
6514               continue;
6515             }
6516             a[k] += values[off + dof - k - 1];
6517           }
6518         }
6519       }
6520     }
6521   }
6522   PetscCall(VecRestoreArray(v, &array));
6523   PetscFunctionReturn(0);
6524 }
6525 
6526 /*@C
6527   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6528 
6529   Not collective
6530 
6531   Input Parameters:
6532 + dm - The `DM`
6533 . section - The section describing the layout in v, or NULL to use the default section
6534 . v - The local vector
6535 . point - The point in the DM
6536 . values - The array of values
6537 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6538          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6539 
6540   Level: intermediate
6541 
6542 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6543 @*/
6544 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6545 {
6546   PetscSection    clSection;
6547   IS              clPoints;
6548   PetscScalar    *array;
6549   PetscInt       *points = NULL;
6550   const PetscInt *clp, *clperm = NULL;
6551   PetscInt        depth, numFields, numPoints, p, clsize;
6552 
6553   PetscFunctionBeginHot;
6554   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6555   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6556   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6557   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6558   PetscCall(DMPlexGetDepth(dm, &depth));
6559   PetscCall(PetscSectionGetNumFields(section, &numFields));
6560   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6561     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6562     PetscFunctionReturn(0);
6563   }
6564   /* Get points */
6565   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6566   for (clsize = 0, p = 0; p < numPoints; p++) {
6567     PetscInt dof;
6568     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6569     clsize += dof;
6570   }
6571   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6572   /* Get array */
6573   PetscCall(VecGetArray(v, &array));
6574   /* Get values */
6575   if (numFields > 0) {
6576     PetscInt offset = 0, f;
6577     for (f = 0; f < numFields; ++f) {
6578       const PetscInt    **perms = NULL;
6579       const PetscScalar **flips = NULL;
6580 
6581       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6582       switch (mode) {
6583       case INSERT_VALUES:
6584         for (p = 0; p < numPoints; p++) {
6585           const PetscInt     point = points[2 * p];
6586           const PetscInt    *perm  = perms ? perms[p] : NULL;
6587           const PetscScalar *flip  = flips ? flips[p] : NULL;
6588           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6589         }
6590         break;
6591       case INSERT_ALL_VALUES:
6592         for (p = 0; p < numPoints; p++) {
6593           const PetscInt     point = points[2 * p];
6594           const PetscInt    *perm  = perms ? perms[p] : NULL;
6595           const PetscScalar *flip  = flips ? flips[p] : NULL;
6596           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6597         }
6598         break;
6599       case INSERT_BC_VALUES:
6600         for (p = 0; p < numPoints; p++) {
6601           const PetscInt     point = points[2 * p];
6602           const PetscInt    *perm  = perms ? perms[p] : NULL;
6603           const PetscScalar *flip  = flips ? flips[p] : NULL;
6604           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6605         }
6606         break;
6607       case ADD_VALUES:
6608         for (p = 0; p < numPoints; p++) {
6609           const PetscInt     point = points[2 * p];
6610           const PetscInt    *perm  = perms ? perms[p] : NULL;
6611           const PetscScalar *flip  = flips ? flips[p] : NULL;
6612           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6613         }
6614         break;
6615       case ADD_ALL_VALUES:
6616         for (p = 0; p < numPoints; p++) {
6617           const PetscInt     point = points[2 * p];
6618           const PetscInt    *perm  = perms ? perms[p] : NULL;
6619           const PetscScalar *flip  = flips ? flips[p] : NULL;
6620           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6621         }
6622         break;
6623       case ADD_BC_VALUES:
6624         for (p = 0; p < numPoints; p++) {
6625           const PetscInt     point = points[2 * p];
6626           const PetscInt    *perm  = perms ? perms[p] : NULL;
6627           const PetscScalar *flip  = flips ? flips[p] : NULL;
6628           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6629         }
6630         break;
6631       default:
6632         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6633       }
6634       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6635     }
6636   } else {
6637     PetscInt            dof, off;
6638     const PetscInt    **perms = NULL;
6639     const PetscScalar **flips = NULL;
6640 
6641     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6642     switch (mode) {
6643     case INSERT_VALUES:
6644       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6645         const PetscInt     point = points[2 * p];
6646         const PetscInt    *perm  = perms ? perms[p] : NULL;
6647         const PetscScalar *flip  = flips ? flips[p] : NULL;
6648         PetscCall(PetscSectionGetDof(section, point, &dof));
6649         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6650       }
6651       break;
6652     case INSERT_ALL_VALUES:
6653       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6654         const PetscInt     point = points[2 * p];
6655         const PetscInt    *perm  = perms ? perms[p] : NULL;
6656         const PetscScalar *flip  = flips ? flips[p] : NULL;
6657         PetscCall(PetscSectionGetDof(section, point, &dof));
6658         updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array);
6659       }
6660       break;
6661     case INSERT_BC_VALUES:
6662       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6663         const PetscInt     point = points[2 * p];
6664         const PetscInt    *perm  = perms ? perms[p] : NULL;
6665         const PetscScalar *flip  = flips ? flips[p] : NULL;
6666         PetscCall(PetscSectionGetDof(section, point, &dof));
6667         updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array);
6668       }
6669       break;
6670     case ADD_VALUES:
6671       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6672         const PetscInt     point = points[2 * p];
6673         const PetscInt    *perm  = perms ? perms[p] : NULL;
6674         const PetscScalar *flip  = flips ? flips[p] : NULL;
6675         PetscCall(PetscSectionGetDof(section, point, &dof));
6676         updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array);
6677       }
6678       break;
6679     case ADD_ALL_VALUES:
6680       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6681         const PetscInt     point = points[2 * p];
6682         const PetscInt    *perm  = perms ? perms[p] : NULL;
6683         const PetscScalar *flip  = flips ? flips[p] : NULL;
6684         PetscCall(PetscSectionGetDof(section, point, &dof));
6685         updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array);
6686       }
6687       break;
6688     case ADD_BC_VALUES:
6689       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6690         const PetscInt     point = points[2 * p];
6691         const PetscInt    *perm  = perms ? perms[p] : NULL;
6692         const PetscScalar *flip  = flips ? flips[p] : NULL;
6693         PetscCall(PetscSectionGetDof(section, point, &dof));
6694         updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array);
6695       }
6696       break;
6697     default:
6698       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6699     }
6700     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6701   }
6702   /* Cleanup points */
6703   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6704   /* Cleanup array */
6705   PetscCall(VecRestoreArray(v, &array));
6706   PetscFunctionReturn(0);
6707 }
6708 
6709 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6710 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6711 {
6712   PetscFunctionBegin;
6713   *contains = PETSC_TRUE;
6714   if (label) {
6715     PetscInt fdof;
6716 
6717     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6718     if (!*contains) {
6719       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6720       *offset += fdof;
6721       PetscFunctionReturn(0);
6722     }
6723   }
6724   PetscFunctionReturn(0);
6725 }
6726 
6727 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6728 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)
6729 {
6730   PetscSection    clSection;
6731   IS              clPoints;
6732   PetscScalar    *array;
6733   PetscInt       *points = NULL;
6734   const PetscInt *clp;
6735   PetscInt        numFields, numPoints, p;
6736   PetscInt        offset = 0, f;
6737 
6738   PetscFunctionBeginHot;
6739   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6740   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6741   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6742   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6743   PetscCall(PetscSectionGetNumFields(section, &numFields));
6744   /* Get points */
6745   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6746   /* Get array */
6747   PetscCall(VecGetArray(v, &array));
6748   /* Get values */
6749   for (f = 0; f < numFields; ++f) {
6750     const PetscInt    **perms = NULL;
6751     const PetscScalar **flips = NULL;
6752     PetscBool           contains;
6753 
6754     if (!fieldActive[f]) {
6755       for (p = 0; p < numPoints * 2; p += 2) {
6756         PetscInt fdof;
6757         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6758         offset += fdof;
6759       }
6760       continue;
6761     }
6762     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6763     switch (mode) {
6764     case INSERT_VALUES:
6765       for (p = 0; p < numPoints; p++) {
6766         const PetscInt     point = points[2 * p];
6767         const PetscInt    *perm  = perms ? perms[p] : NULL;
6768         const PetscScalar *flip  = flips ? flips[p] : NULL;
6769         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6770         if (!contains) continue;
6771         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6772       }
6773       break;
6774     case INSERT_ALL_VALUES:
6775       for (p = 0; p < numPoints; p++) {
6776         const PetscInt     point = points[2 * p];
6777         const PetscInt    *perm  = perms ? perms[p] : NULL;
6778         const PetscScalar *flip  = flips ? flips[p] : NULL;
6779         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6780         if (!contains) continue;
6781         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6782       }
6783       break;
6784     case INSERT_BC_VALUES:
6785       for (p = 0; p < numPoints; p++) {
6786         const PetscInt     point = points[2 * p];
6787         const PetscInt    *perm  = perms ? perms[p] : NULL;
6788         const PetscScalar *flip  = flips ? flips[p] : NULL;
6789         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6790         if (!contains) continue;
6791         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6792       }
6793       break;
6794     case ADD_VALUES:
6795       for (p = 0; p < numPoints; p++) {
6796         const PetscInt     point = points[2 * p];
6797         const PetscInt    *perm  = perms ? perms[p] : NULL;
6798         const PetscScalar *flip  = flips ? flips[p] : NULL;
6799         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6800         if (!contains) continue;
6801         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6802       }
6803       break;
6804     case ADD_ALL_VALUES:
6805       for (p = 0; p < numPoints; p++) {
6806         const PetscInt     point = points[2 * p];
6807         const PetscInt    *perm  = perms ? perms[p] : NULL;
6808         const PetscScalar *flip  = flips ? flips[p] : NULL;
6809         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6810         if (!contains) continue;
6811         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6812       }
6813       break;
6814     default:
6815       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6816     }
6817     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6818   }
6819   /* Cleanup points */
6820   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6821   /* Cleanup array */
6822   PetscCall(VecRestoreArray(v, &array));
6823   PetscFunctionReturn(0);
6824 }
6825 
6826 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6827 {
6828   PetscMPIInt rank;
6829   PetscInt    i, j;
6830 
6831   PetscFunctionBegin;
6832   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6833   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6834   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6835   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6836   numCIndices = numCIndices ? numCIndices : numRIndices;
6837   if (!values) PetscFunctionReturn(0);
6838   for (i = 0; i < numRIndices; i++) {
6839     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6840     for (j = 0; j < numCIndices; j++) {
6841 #if defined(PETSC_USE_COMPLEX)
6842       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6843 #else
6844       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6845 #endif
6846     }
6847     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6848   }
6849   PetscFunctionReturn(0);
6850 }
6851 
6852 /*
6853   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6854 
6855   Input Parameters:
6856 + section - The section for this data layout
6857 . islocal - Is the section (and thus indices being requested) local or global?
6858 . point   - The point contributing dofs with these indices
6859 . off     - The global offset of this point
6860 . loff    - The local offset of each field
6861 . setBC   - The flag determining whether to include indices of boundary values
6862 . perm    - A permutation of the dofs on this point, or NULL
6863 - indperm - A permutation of the entire indices array, or NULL
6864 
6865   Output Parameter:
6866 . indices - Indices for dofs on this point
6867 
6868   Level: developer
6869 
6870   Note: The indices could be local or global, depending on the value of 'off'.
6871 */
6872 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6873 {
6874   PetscInt        dof;   /* The number of unknowns on this point */
6875   PetscInt        cdof;  /* The number of constraints on this point */
6876   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6877   PetscInt        cind = 0, k;
6878 
6879   PetscFunctionBegin;
6880   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6881   PetscCall(PetscSectionGetDof(section, point, &dof));
6882   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6883   if (!cdof || setBC) {
6884     for (k = 0; k < dof; ++k) {
6885       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6886       const PetscInt ind    = indperm ? indperm[preind] : preind;
6887 
6888       indices[ind] = off + k;
6889     }
6890   } else {
6891     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6892     for (k = 0; k < dof; ++k) {
6893       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6894       const PetscInt ind    = indperm ? indperm[preind] : preind;
6895 
6896       if ((cind < cdof) && (k == cdofs[cind])) {
6897         /* Insert check for returning constrained indices */
6898         indices[ind] = -(off + k + 1);
6899         ++cind;
6900       } else {
6901         indices[ind] = off + k - (islocal ? 0 : cind);
6902       }
6903     }
6904   }
6905   *loff += dof;
6906   PetscFunctionReturn(0);
6907 }
6908 
6909 /*
6910  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6911 
6912  Input Parameters:
6913 + section - a section (global or local)
6914 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6915 . point - point within section
6916 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6917 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6918 . setBC - identify constrained (boundary condition) points via involution.
6919 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6920 . permsoff - offset
6921 - indperm - index permutation
6922 
6923  Output Parameter:
6924 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6925 . indices - array to hold indices (as defined by section) of each dof associated with point
6926 
6927  Notes:
6928  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6929  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6930  in the local vector.
6931 
6932  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6933  significant).  It is invalid to call with a global section and setBC=true.
6934 
6935  Developer Note:
6936  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6937  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6938  offset could be obtained from the section instead of passing it explicitly as we do now.
6939 
6940  Example:
6941  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6942  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6943  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6944  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.
6945 
6946  Level: developer
6947 */
6948 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[])
6949 {
6950   PetscInt numFields, foff, f;
6951 
6952   PetscFunctionBegin;
6953   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6954   PetscCall(PetscSectionGetNumFields(section, &numFields));
6955   for (f = 0, foff = 0; f < numFields; ++f) {
6956     PetscInt        fdof, cfdof;
6957     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6958     PetscInt        cind = 0, b;
6959     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6960 
6961     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6962     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6963     if (!cfdof || setBC) {
6964       for (b = 0; b < fdof; ++b) {
6965         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6966         const PetscInt ind    = indperm ? indperm[preind] : preind;
6967 
6968         indices[ind] = off + foff + b;
6969       }
6970     } else {
6971       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6972       for (b = 0; b < fdof; ++b) {
6973         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6974         const PetscInt ind    = indperm ? indperm[preind] : preind;
6975 
6976         if ((cind < cfdof) && (b == fcdofs[cind])) {
6977           indices[ind] = -(off + foff + b + 1);
6978           ++cind;
6979         } else {
6980           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6981         }
6982       }
6983     }
6984     foff += (setBC || islocal ? fdof : (fdof - cfdof));
6985     foffs[f] += fdof;
6986   }
6987   PetscFunctionReturn(0);
6988 }
6989 
6990 /*
6991   This version believes the globalSection offsets for each field, rather than just the point offset
6992 
6993  . foffs - The offset into 'indices' for each field, since it is segregated by field
6994 
6995  Notes:
6996  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6997  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6998 */
6999 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7000 {
7001   PetscInt numFields, foff, f;
7002 
7003   PetscFunctionBegin;
7004   PetscCall(PetscSectionGetNumFields(section, &numFields));
7005   for (f = 0; f < numFields; ++f) {
7006     PetscInt        fdof, cfdof;
7007     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7008     PetscInt        cind = 0, b;
7009     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7010 
7011     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7012     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7013     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7014     if (!cfdof) {
7015       for (b = 0; b < fdof; ++b) {
7016         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7017         const PetscInt ind    = indperm ? indperm[preind] : preind;
7018 
7019         indices[ind] = foff + b;
7020       }
7021     } else {
7022       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7023       for (b = 0; b < fdof; ++b) {
7024         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7025         const PetscInt ind    = indperm ? indperm[preind] : preind;
7026 
7027         if ((cind < cfdof) && (b == fcdofs[cind])) {
7028           indices[ind] = -(foff + b + 1);
7029           ++cind;
7030         } else {
7031           indices[ind] = foff + b - cind;
7032         }
7033       }
7034     }
7035     foffs[f] += fdof;
7036   }
7037   PetscFunctionReturn(0);
7038 }
7039 
7040 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)
7041 {
7042   Mat             cMat;
7043   PetscSection    aSec, cSec;
7044   IS              aIS;
7045   PetscInt        aStart = -1, aEnd = -1;
7046   const PetscInt *anchors;
7047   PetscInt        numFields, f, p, q, newP = 0;
7048   PetscInt        newNumPoints = 0, newNumIndices = 0;
7049   PetscInt       *newPoints, *indices, *newIndices;
7050   PetscInt        maxAnchor, maxDof;
7051   PetscInt        newOffsets[32];
7052   PetscInt       *pointMatOffsets[32];
7053   PetscInt       *newPointOffsets[32];
7054   PetscScalar    *pointMat[32];
7055   PetscScalar    *newValues      = NULL, *tmpValues;
7056   PetscBool       anyConstrained = PETSC_FALSE;
7057 
7058   PetscFunctionBegin;
7059   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7060   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7061   PetscCall(PetscSectionGetNumFields(section, &numFields));
7062 
7063   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7064   /* if there are point-to-point constraints */
7065   if (aSec) {
7066     PetscCall(PetscArrayzero(newOffsets, 32));
7067     PetscCall(ISGetIndices(aIS, &anchors));
7068     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7069     /* figure out how many points are going to be in the new element matrix
7070      * (we allow double counting, because it's all just going to be summed
7071      * into the global matrix anyway) */
7072     for (p = 0; p < 2 * numPoints; p += 2) {
7073       PetscInt b    = points[p];
7074       PetscInt bDof = 0, bSecDof;
7075 
7076       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7077       if (!bSecDof) continue;
7078       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7079       if (bDof) {
7080         /* this point is constrained */
7081         /* it is going to be replaced by its anchors */
7082         PetscInt bOff, q;
7083 
7084         anyConstrained = PETSC_TRUE;
7085         newNumPoints += bDof;
7086         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7087         for (q = 0; q < bDof; q++) {
7088           PetscInt a = anchors[bOff + q];
7089           PetscInt aDof;
7090 
7091           PetscCall(PetscSectionGetDof(section, a, &aDof));
7092           newNumIndices += aDof;
7093           for (f = 0; f < numFields; ++f) {
7094             PetscInt fDof;
7095 
7096             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7097             newOffsets[f + 1] += fDof;
7098           }
7099         }
7100       } else {
7101         /* this point is not constrained */
7102         newNumPoints++;
7103         newNumIndices += bSecDof;
7104         for (f = 0; f < numFields; ++f) {
7105           PetscInt fDof;
7106 
7107           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7108           newOffsets[f + 1] += fDof;
7109         }
7110       }
7111     }
7112   }
7113   if (!anyConstrained) {
7114     if (outNumPoints) *outNumPoints = 0;
7115     if (outNumIndices) *outNumIndices = 0;
7116     if (outPoints) *outPoints = NULL;
7117     if (outValues) *outValues = NULL;
7118     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7119     PetscFunctionReturn(0);
7120   }
7121 
7122   if (outNumPoints) *outNumPoints = newNumPoints;
7123   if (outNumIndices) *outNumIndices = newNumIndices;
7124 
7125   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7126 
7127   if (!outPoints && !outValues) {
7128     if (offsets) {
7129       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7130     }
7131     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7132     PetscFunctionReturn(0);
7133   }
7134 
7135   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7136 
7137   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7138 
7139   /* workspaces */
7140   if (numFields) {
7141     for (f = 0; f < numFields; f++) {
7142       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7143       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7144     }
7145   } else {
7146     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7147     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7148   }
7149 
7150   /* get workspaces for the point-to-point matrices */
7151   if (numFields) {
7152     PetscInt totalOffset, totalMatOffset;
7153 
7154     for (p = 0; p < numPoints; p++) {
7155       PetscInt b    = points[2 * p];
7156       PetscInt bDof = 0, bSecDof;
7157 
7158       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7159       if (!bSecDof) {
7160         for (f = 0; f < numFields; f++) {
7161           newPointOffsets[f][p + 1] = 0;
7162           pointMatOffsets[f][p + 1] = 0;
7163         }
7164         continue;
7165       }
7166       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7167       if (bDof) {
7168         for (f = 0; f < numFields; f++) {
7169           PetscInt fDof, q, bOff, allFDof = 0;
7170 
7171           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7172           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7173           for (q = 0; q < bDof; q++) {
7174             PetscInt a = anchors[bOff + q];
7175             PetscInt aFDof;
7176 
7177             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7178             allFDof += aFDof;
7179           }
7180           newPointOffsets[f][p + 1] = allFDof;
7181           pointMatOffsets[f][p + 1] = fDof * allFDof;
7182         }
7183       } else {
7184         for (f = 0; f < numFields; f++) {
7185           PetscInt fDof;
7186 
7187           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7188           newPointOffsets[f][p + 1] = fDof;
7189           pointMatOffsets[f][p + 1] = 0;
7190         }
7191       }
7192     }
7193     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7194       newPointOffsets[f][0] = totalOffset;
7195       pointMatOffsets[f][0] = totalMatOffset;
7196       for (p = 0; p < numPoints; p++) {
7197         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7198         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7199       }
7200       totalOffset    = newPointOffsets[f][numPoints];
7201       totalMatOffset = pointMatOffsets[f][numPoints];
7202       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7203     }
7204   } else {
7205     for (p = 0; p < numPoints; p++) {
7206       PetscInt b    = points[2 * p];
7207       PetscInt bDof = 0, bSecDof;
7208 
7209       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7210       if (!bSecDof) {
7211         newPointOffsets[0][p + 1] = 0;
7212         pointMatOffsets[0][p + 1] = 0;
7213         continue;
7214       }
7215       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7216       if (bDof) {
7217         PetscInt bOff, q, allDof = 0;
7218 
7219         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7220         for (q = 0; q < bDof; q++) {
7221           PetscInt a = anchors[bOff + q], aDof;
7222 
7223           PetscCall(PetscSectionGetDof(section, a, &aDof));
7224           allDof += aDof;
7225         }
7226         newPointOffsets[0][p + 1] = allDof;
7227         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7228       } else {
7229         newPointOffsets[0][p + 1] = bSecDof;
7230         pointMatOffsets[0][p + 1] = 0;
7231       }
7232     }
7233     newPointOffsets[0][0] = 0;
7234     pointMatOffsets[0][0] = 0;
7235     for (p = 0; p < numPoints; p++) {
7236       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7237       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7238     }
7239     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7240   }
7241 
7242   /* output arrays */
7243   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7244 
7245   /* get the point-to-point matrices; construct newPoints */
7246   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7247   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7248   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7249   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7250   if (numFields) {
7251     for (p = 0, newP = 0; p < numPoints; p++) {
7252       PetscInt b    = points[2 * p];
7253       PetscInt o    = points[2 * p + 1];
7254       PetscInt bDof = 0, bSecDof;
7255 
7256       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7257       if (!bSecDof) continue;
7258       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7259       if (bDof) {
7260         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7261 
7262         fStart[0] = 0;
7263         fEnd[0]   = 0;
7264         for (f = 0; f < numFields; f++) {
7265           PetscInt fDof;
7266 
7267           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7268           fStart[f + 1] = fStart[f] + fDof;
7269           fEnd[f + 1]   = fStart[f + 1];
7270         }
7271         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7272         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7273 
7274         fAnchorStart[0] = 0;
7275         fAnchorEnd[0]   = 0;
7276         for (f = 0; f < numFields; f++) {
7277           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7278 
7279           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7280           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7281         }
7282         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7283         for (q = 0; q < bDof; q++) {
7284           PetscInt a = anchors[bOff + q], aOff;
7285 
7286           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7287           newPoints[2 * (newP + q)]     = a;
7288           newPoints[2 * (newP + q) + 1] = 0;
7289           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7290           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7291         }
7292         newP += bDof;
7293 
7294         if (outValues) {
7295           /* get the point-to-point submatrix */
7296           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]));
7297         }
7298       } else {
7299         newPoints[2 * newP]     = b;
7300         newPoints[2 * newP + 1] = o;
7301         newP++;
7302       }
7303     }
7304   } else {
7305     for (p = 0; p < numPoints; p++) {
7306       PetscInt b    = points[2 * p];
7307       PetscInt o    = points[2 * p + 1];
7308       PetscInt bDof = 0, bSecDof;
7309 
7310       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7311       if (!bSecDof) continue;
7312       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7313       if (bDof) {
7314         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7315 
7316         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7317         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7318 
7319         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7320         for (q = 0; q < bDof; q++) {
7321           PetscInt a = anchors[bOff + q], aOff;
7322 
7323           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7324 
7325           newPoints[2 * (newP + q)]     = a;
7326           newPoints[2 * (newP + q) + 1] = 0;
7327           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7328           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7329         }
7330         newP += bDof;
7331 
7332         /* get the point-to-point submatrix */
7333         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7334       } else {
7335         newPoints[2 * newP]     = b;
7336         newPoints[2 * newP + 1] = o;
7337         newP++;
7338       }
7339     }
7340   }
7341 
7342   if (outValues) {
7343     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7344     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7345     /* multiply constraints on the right */
7346     if (numFields) {
7347       for (f = 0; f < numFields; f++) {
7348         PetscInt oldOff = offsets[f];
7349 
7350         for (p = 0; p < numPoints; p++) {
7351           PetscInt cStart = newPointOffsets[f][p];
7352           PetscInt b      = points[2 * p];
7353           PetscInt c, r, k;
7354           PetscInt dof;
7355 
7356           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7357           if (!dof) continue;
7358           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7359             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7360             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7361 
7362             for (r = 0; r < numIndices; r++) {
7363               for (c = 0; c < nCols; c++) {
7364                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7365               }
7366             }
7367           } else {
7368             /* copy this column as is */
7369             for (r = 0; r < numIndices; r++) {
7370               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7371             }
7372           }
7373           oldOff += dof;
7374         }
7375       }
7376     } else {
7377       PetscInt oldOff = 0;
7378       for (p = 0; p < numPoints; p++) {
7379         PetscInt cStart = newPointOffsets[0][p];
7380         PetscInt b      = points[2 * p];
7381         PetscInt c, r, k;
7382         PetscInt dof;
7383 
7384         PetscCall(PetscSectionGetDof(section, b, &dof));
7385         if (!dof) continue;
7386         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7387           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7388           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7389 
7390           for (r = 0; r < numIndices; r++) {
7391             for (c = 0; c < nCols; c++) {
7392               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7393             }
7394           }
7395         } else {
7396           /* copy this column as is */
7397           for (r = 0; r < numIndices; r++) {
7398             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7399           }
7400         }
7401         oldOff += dof;
7402       }
7403     }
7404 
7405     if (multiplyLeft) {
7406       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7407       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7408       /* multiply constraints transpose on the left */
7409       if (numFields) {
7410         for (f = 0; f < numFields; f++) {
7411           PetscInt oldOff = offsets[f];
7412 
7413           for (p = 0; p < numPoints; p++) {
7414             PetscInt rStart = newPointOffsets[f][p];
7415             PetscInt b      = points[2 * p];
7416             PetscInt c, r, k;
7417             PetscInt dof;
7418 
7419             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7420             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7421               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7422               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7423 
7424               for (r = 0; r < nRows; r++) {
7425                 for (c = 0; c < newNumIndices; c++) {
7426                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7427                 }
7428               }
7429             } else {
7430               /* copy this row as is */
7431               for (r = 0; r < dof; r++) {
7432                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7433               }
7434             }
7435             oldOff += dof;
7436           }
7437         }
7438       } else {
7439         PetscInt oldOff = 0;
7440 
7441         for (p = 0; p < numPoints; p++) {
7442           PetscInt rStart = newPointOffsets[0][p];
7443           PetscInt b      = points[2 * p];
7444           PetscInt c, r, k;
7445           PetscInt dof;
7446 
7447           PetscCall(PetscSectionGetDof(section, b, &dof));
7448           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7449             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7450             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7451 
7452             for (r = 0; r < nRows; r++) {
7453               for (c = 0; c < newNumIndices; c++) {
7454                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7455               }
7456             }
7457           } else {
7458             /* copy this row as is */
7459             for (r = 0; r < dof; r++) {
7460               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7461             }
7462           }
7463           oldOff += dof;
7464         }
7465       }
7466 
7467       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7468     } else {
7469       newValues = tmpValues;
7470     }
7471   }
7472 
7473   /* clean up */
7474   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7475   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7476 
7477   if (numFields) {
7478     for (f = 0; f < numFields; f++) {
7479       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7480       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7481       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7482     }
7483   } else {
7484     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7485     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7486     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7487   }
7488   PetscCall(ISRestoreIndices(aIS, &anchors));
7489 
7490   /* output */
7491   if (outPoints) {
7492     *outPoints = newPoints;
7493   } else {
7494     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7495   }
7496   if (outValues) *outValues = newValues;
7497   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7498   PetscFunctionReturn(0);
7499 }
7500 
7501 /*@C
7502   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7503 
7504   Not collective
7505 
7506   Input Parameters:
7507 + dm         - The `DM`
7508 . section    - The `PetscSection` describing the points (a local section)
7509 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7510 . point      - The point defining the closure
7511 - useClPerm  - Use the closure point permutation if available
7512 
7513   Output Parameters:
7514 + numIndices - The number of dof indices in the closure of point with the input sections
7515 . indices    - The dof indices
7516 . outOffsets - Array to write the field offsets into, or NULL
7517 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7518 
7519   Level: advanced
7520 
7521   Notes:
7522   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7523 
7524   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7525   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7526   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7527   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7528   indices (with the above semantics) are implied.
7529 
7530 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7531           `PetscSection`, `DMGetGlobalSection()`
7532 @*/
7533 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7534 {
7535   /* Closure ordering */
7536   PetscSection    clSection;
7537   IS              clPoints;
7538   const PetscInt *clp;
7539   PetscInt       *points;
7540   const PetscInt *clperm = NULL;
7541   /* Dof permutation and sign flips */
7542   const PetscInt    **perms[32] = {NULL};
7543   const PetscScalar **flips[32] = {NULL};
7544   PetscScalar        *valCopy   = NULL;
7545   /* Hanging node constraints */
7546   PetscInt    *pointsC = NULL;
7547   PetscScalar *valuesC = NULL;
7548   PetscInt     NclC, NiC;
7549 
7550   PetscInt *idx;
7551   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7552   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7553 
7554   PetscFunctionBeginHot;
7555   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7556   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7557   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7558   if (numIndices) PetscValidIntPointer(numIndices, 6);
7559   if (indices) PetscValidPointer(indices, 7);
7560   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7561   if (values) PetscValidPointer(values, 9);
7562   PetscCall(PetscSectionGetNumFields(section, &Nf));
7563   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7564   PetscCall(PetscArrayzero(offsets, 32));
7565   /* 1) Get points in closure */
7566   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7567   if (useClPerm) {
7568     PetscInt depth, clsize;
7569     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7570     for (clsize = 0, p = 0; p < Ncl; p++) {
7571       PetscInt dof;
7572       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7573       clsize += dof;
7574     }
7575     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7576   }
7577   /* 2) Get number of indices on these points and field offsets from section */
7578   for (p = 0; p < Ncl * 2; p += 2) {
7579     PetscInt dof, fdof;
7580 
7581     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7582     for (f = 0; f < Nf; ++f) {
7583       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7584       offsets[f + 1] += fdof;
7585     }
7586     Ni += dof;
7587   }
7588   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7589   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7590   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7591   for (f = 0; f < PetscMax(1, Nf); ++f) {
7592     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7593     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7594     /* may need to apply sign changes to the element matrix */
7595     if (values && flips[f]) {
7596       PetscInt foffset = offsets[f];
7597 
7598       for (p = 0; p < Ncl; ++p) {
7599         PetscInt           pnt  = points[2 * p], fdof;
7600         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7601 
7602         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7603         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7604         if (flip) {
7605           PetscInt i, j, k;
7606 
7607           if (!valCopy) {
7608             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7609             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7610             *values = valCopy;
7611           }
7612           for (i = 0; i < fdof; ++i) {
7613             PetscScalar fval = flip[i];
7614 
7615             for (k = 0; k < Ni; ++k) {
7616               valCopy[Ni * (foffset + i) + k] *= fval;
7617               valCopy[Ni * k + (foffset + i)] *= fval;
7618             }
7619           }
7620         }
7621         foffset += fdof;
7622       }
7623     }
7624   }
7625   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7626   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7627   if (NclC) {
7628     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7629     for (f = 0; f < PetscMax(1, Nf); ++f) {
7630       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7631       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7632     }
7633     for (f = 0; f < PetscMax(1, Nf); ++f) {
7634       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7635       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7636     }
7637     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7638     Ncl    = NclC;
7639     Ni     = NiC;
7640     points = pointsC;
7641     if (values) *values = valuesC;
7642   }
7643   /* 5) Calculate indices */
7644   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7645   if (Nf) {
7646     PetscInt  idxOff;
7647     PetscBool useFieldOffsets;
7648 
7649     if (outOffsets) {
7650       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7651     }
7652     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7653     if (useFieldOffsets) {
7654       for (p = 0; p < Ncl; ++p) {
7655         const PetscInt pnt = points[p * 2];
7656 
7657         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7658       }
7659     } else {
7660       for (p = 0; p < Ncl; ++p) {
7661         const PetscInt pnt = points[p * 2];
7662 
7663         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7664         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7665          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7666          * global section. */
7667         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7668       }
7669     }
7670   } else {
7671     PetscInt off = 0, idxOff;
7672 
7673     for (p = 0; p < Ncl; ++p) {
7674       const PetscInt  pnt  = points[p * 2];
7675       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7676 
7677       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7678       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7679        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7680       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7681     }
7682   }
7683   /* 6) Cleanup */
7684   for (f = 0; f < PetscMax(1, Nf); ++f) {
7685     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7686     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7687   }
7688   if (NclC) {
7689     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7690   } else {
7691     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7692   }
7693 
7694   if (numIndices) *numIndices = Ni;
7695   if (indices) *indices = idx;
7696   PetscFunctionReturn(0);
7697 }
7698 
7699 /*@C
7700   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7701 
7702   Not collective
7703 
7704   Input Parameters:
7705 + dm         - The `DM`
7706 . section    - The `PetscSection` describing the points (a local section)
7707 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7708 . point      - The point defining the closure
7709 - useClPerm  - Use the closure point permutation if available
7710 
7711   Output Parameters:
7712 + numIndices - The number of dof indices in the closure of point with the input sections
7713 . indices    - The dof indices
7714 . outOffsets - Array to write the field offsets into, or NULL
7715 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7716 
7717   Level: advanced
7718 
7719   Notes:
7720   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7721 
7722   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7723   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7724   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7725   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7726   indices (with the above semantics) are implied.
7727 
7728 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7729 @*/
7730 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7731 {
7732   PetscFunctionBegin;
7733   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7734   PetscValidPointer(indices, 7);
7735   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7736   PetscFunctionReturn(0);
7737 }
7738 
7739 /*@C
7740   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7741 
7742   Not collective
7743 
7744   Input Parameters:
7745 + dm - The `DM`
7746 . section - The section describing the layout in v, or NULL to use the default section
7747 . globalSection - The section describing the layout in v, or NULL to use the default global section
7748 . A - The matrix
7749 . point - The point in the `DM`
7750 . values - The array of values
7751 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7752 
7753   Level: intermediate
7754 
7755 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7756 @*/
7757 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7758 {
7759   DM_Plex           *mesh = (DM_Plex *)dm->data;
7760   PetscInt          *indices;
7761   PetscInt           numIndices;
7762   const PetscScalar *valuesOrig = values;
7763   PetscErrorCode     ierr;
7764 
7765   PetscFunctionBegin;
7766   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7767   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7768   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7769   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7770   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7771   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7772 
7773   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7774 
7775   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7776   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7777   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7778   if (ierr) {
7779     PetscMPIInt rank;
7780 
7781     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7782     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7783     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7784     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7785     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7786     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7787   }
7788   if (mesh->printFEM > 1) {
7789     PetscInt i;
7790     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7791     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7792     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7793   }
7794 
7795   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7796   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7797   PetscFunctionReturn(0);
7798 }
7799 
7800 /*@C
7801   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7802 
7803   Not collective
7804 
7805   Input Parameters:
7806 + dmRow - The `DM` for the row fields
7807 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7808 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7809 . dmCol - The `DM` for the column fields
7810 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7811 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7812 . A - The matrix
7813 . point - The point in the `DM`
7814 . values - The array of values
7815 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7816 
7817   Level: intermediate
7818 
7819 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7820 @*/
7821 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7822 {
7823   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7824   PetscInt          *indicesRow, *indicesCol;
7825   PetscInt           numIndicesRow, numIndicesCol;
7826   const PetscScalar *valuesOrig = values;
7827   PetscErrorCode     ierr;
7828 
7829   PetscFunctionBegin;
7830   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7831   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7832   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7833   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7834   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7835   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7836   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7837   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7838   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7839   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7840   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7841 
7842   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7843   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7844 
7845   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7846   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7847   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7848   if (ierr) {
7849     PetscMPIInt rank;
7850 
7851     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7852     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7853     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7854     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7855     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7856     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7857   }
7858 
7859   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7860   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7861   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7862   PetscFunctionReturn(0);
7863 }
7864 
7865 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7866 {
7867   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7868   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7869   PetscInt       *cpoints = NULL;
7870   PetscInt       *findices, *cindices;
7871   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7872   PetscInt        foffsets[32], coffsets[32];
7873   DMPolytopeType  ct;
7874   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7875   PetscErrorCode  ierr;
7876 
7877   PetscFunctionBegin;
7878   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7879   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7880   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7881   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7882   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7883   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7884   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7885   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7886   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7887   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7888   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7889   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7890   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7891   PetscCall(PetscArrayzero(foffsets, 32));
7892   PetscCall(PetscArrayzero(coffsets, 32));
7893   /* Column indices */
7894   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7895   maxFPoints = numCPoints;
7896   /* Compress out points not in the section */
7897   /*   TODO: Squeeze out points with 0 dof as well */
7898   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7899   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7900     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7901       cpoints[q * 2]     = cpoints[p];
7902       cpoints[q * 2 + 1] = cpoints[p + 1];
7903       ++q;
7904     }
7905   }
7906   numCPoints = q;
7907   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7908     PetscInt fdof;
7909 
7910     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7911     if (!dof) continue;
7912     for (f = 0; f < numFields; ++f) {
7913       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7914       coffsets[f + 1] += fdof;
7915     }
7916     numCIndices += dof;
7917   }
7918   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7919   /* Row indices */
7920   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7921   {
7922     DMPlexTransform tr;
7923     DMPolytopeType *rct;
7924     PetscInt       *rsize, *rcone, *rornt, Nt;
7925 
7926     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7927     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7928     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7929     numSubcells = rsize[Nt - 1];
7930     PetscCall(DMPlexTransformDestroy(&tr));
7931   }
7932   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7933   for (r = 0, q = 0; r < numSubcells; ++r) {
7934     /* TODO Map from coarse to fine cells */
7935     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7936     /* Compress out points not in the section */
7937     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7938     for (p = 0; p < numFPoints * 2; p += 2) {
7939       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7940         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7941         if (!dof) continue;
7942         for (s = 0; s < q; ++s)
7943           if (fpoints[p] == ftotpoints[s * 2]) break;
7944         if (s < q) continue;
7945         ftotpoints[q * 2]     = fpoints[p];
7946         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7947         ++q;
7948       }
7949     }
7950     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7951   }
7952   numFPoints = q;
7953   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7954     PetscInt fdof;
7955 
7956     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7957     if (!dof) continue;
7958     for (f = 0; f < numFields; ++f) {
7959       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7960       foffsets[f + 1] += fdof;
7961     }
7962     numFIndices += dof;
7963   }
7964   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7965 
7966   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7967   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7968   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7969   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7970   if (numFields) {
7971     const PetscInt **permsF[32] = {NULL};
7972     const PetscInt **permsC[32] = {NULL};
7973 
7974     for (f = 0; f < numFields; f++) {
7975       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7976       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7977     }
7978     for (p = 0; p < numFPoints; p++) {
7979       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7980       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7981     }
7982     for (p = 0; p < numCPoints; p++) {
7983       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7984       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7985     }
7986     for (f = 0; f < numFields; f++) {
7987       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7988       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7989     }
7990   } else {
7991     const PetscInt **permsF = NULL;
7992     const PetscInt **permsC = NULL;
7993 
7994     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7995     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7996     for (p = 0, off = 0; p < numFPoints; p++) {
7997       const PetscInt *perm = permsF ? permsF[p] : NULL;
7998 
7999       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8000       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8001     }
8002     for (p = 0, off = 0; p < numCPoints; p++) {
8003       const PetscInt *perm = permsC ? permsC[p] : NULL;
8004 
8005       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8006       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8007     }
8008     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8009     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8010   }
8011   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8012   /* TODO: flips */
8013   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8014   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8015   if (ierr) {
8016     PetscMPIInt rank;
8017 
8018     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8019     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8020     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8021     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8022     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8023   }
8024   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8025   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8026   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8027   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8028   PetscFunctionReturn(0);
8029 }
8030 
8031 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8032 {
8033   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8034   PetscInt       *cpoints = NULL;
8035   PetscInt        foffsets[32], coffsets[32];
8036   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8037   DMPolytopeType  ct;
8038   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8039 
8040   PetscFunctionBegin;
8041   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8042   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8043   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8044   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8045   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8046   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8047   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8048   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8049   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8050   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8051   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8052   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8053   PetscCall(PetscArrayzero(foffsets, 32));
8054   PetscCall(PetscArrayzero(coffsets, 32));
8055   /* Column indices */
8056   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8057   maxFPoints = numCPoints;
8058   /* Compress out points not in the section */
8059   /*   TODO: Squeeze out points with 0 dof as well */
8060   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8061   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8062     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8063       cpoints[q * 2]     = cpoints[p];
8064       cpoints[q * 2 + 1] = cpoints[p + 1];
8065       ++q;
8066     }
8067   }
8068   numCPoints = q;
8069   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8070     PetscInt fdof;
8071 
8072     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8073     if (!dof) continue;
8074     for (f = 0; f < numFields; ++f) {
8075       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8076       coffsets[f + 1] += fdof;
8077     }
8078     numCIndices += dof;
8079   }
8080   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8081   /* Row indices */
8082   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8083   {
8084     DMPlexTransform tr;
8085     DMPolytopeType *rct;
8086     PetscInt       *rsize, *rcone, *rornt, Nt;
8087 
8088     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8089     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8090     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8091     numSubcells = rsize[Nt - 1];
8092     PetscCall(DMPlexTransformDestroy(&tr));
8093   }
8094   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8095   for (r = 0, q = 0; r < numSubcells; ++r) {
8096     /* TODO Map from coarse to fine cells */
8097     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8098     /* Compress out points not in the section */
8099     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8100     for (p = 0; p < numFPoints * 2; p += 2) {
8101       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8102         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8103         if (!dof) continue;
8104         for (s = 0; s < q; ++s)
8105           if (fpoints[p] == ftotpoints[s * 2]) break;
8106         if (s < q) continue;
8107         ftotpoints[q * 2]     = fpoints[p];
8108         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8109         ++q;
8110       }
8111     }
8112     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8113   }
8114   numFPoints = q;
8115   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8116     PetscInt fdof;
8117 
8118     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8119     if (!dof) continue;
8120     for (f = 0; f < numFields; ++f) {
8121       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8122       foffsets[f + 1] += fdof;
8123     }
8124     numFIndices += dof;
8125   }
8126   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8127 
8128   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8129   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8130   if (numFields) {
8131     const PetscInt **permsF[32] = {NULL};
8132     const PetscInt **permsC[32] = {NULL};
8133 
8134     for (f = 0; f < numFields; f++) {
8135       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8136       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8137     }
8138     for (p = 0; p < numFPoints; p++) {
8139       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8140       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8141     }
8142     for (p = 0; p < numCPoints; p++) {
8143       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8144       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8145     }
8146     for (f = 0; f < numFields; f++) {
8147       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8148       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8149     }
8150   } else {
8151     const PetscInt **permsF = NULL;
8152     const PetscInt **permsC = NULL;
8153 
8154     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8155     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8156     for (p = 0, off = 0; p < numFPoints; p++) {
8157       const PetscInt *perm = permsF ? permsF[p] : NULL;
8158 
8159       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8160       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8161     }
8162     for (p = 0, off = 0; p < numCPoints; p++) {
8163       const PetscInt *perm = permsC ? permsC[p] : NULL;
8164 
8165       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8166       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8167     }
8168     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8169     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8170   }
8171   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8172   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8173   PetscFunctionReturn(0);
8174 }
8175 
8176 /*@C
8177   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8178 
8179   Input Parameter:
8180 . dm   - The `DMPLEX` object
8181 
8182   Output Parameter:
8183 . cellHeight - The height of a cell
8184 
8185   Level: developer
8186 
8187 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`,  `DMPlexSetVTKCellHeight()`
8188 @*/
8189 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8190 {
8191   DM_Plex *mesh = (DM_Plex *)dm->data;
8192 
8193   PetscFunctionBegin;
8194   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8195   PetscValidIntPointer(cellHeight, 2);
8196   *cellHeight = mesh->vtkCellHeight;
8197   PetscFunctionReturn(0);
8198 }
8199 
8200 /*@C
8201   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8202 
8203   Input Parameters:
8204 + dm   - The `DMPLEX` object
8205 - cellHeight - The height of a cell
8206 
8207   Level: developer
8208 
8209 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8210 @*/
8211 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8212 {
8213   DM_Plex *mesh = (DM_Plex *)dm->data;
8214 
8215   PetscFunctionBegin;
8216   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8217   mesh->vtkCellHeight = cellHeight;
8218   PetscFunctionReturn(0);
8219 }
8220 
8221 /*@
8222   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8223 
8224   Input Parameter:
8225 . dm - The `DMPLEX` object
8226 
8227   Output Parameters:
8228 + gcStart - The first ghost cell, or NULL
8229 - gcEnd   - The upper bound on ghost cells, or NULL
8230 
8231   Level: advanced
8232 
8233 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8234 @*/
8235 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8236 {
8237   DMLabel ctLabel;
8238 
8239   PetscFunctionBegin;
8240   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8241   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8242   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8243   // Reset label for fast lookup
8244   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8245   PetscFunctionReturn(0);
8246 }
8247 
8248 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8249 {
8250   PetscSection section, globalSection;
8251   PetscInt    *numbers, p;
8252 
8253   PetscFunctionBegin;
8254   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8255   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8256   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8257   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8258   PetscCall(PetscSectionSetUp(section));
8259   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8260   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8261   for (p = pStart; p < pEnd; ++p) {
8262     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8263     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8264     else numbers[p - pStart] += shift;
8265   }
8266   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8267   if (globalSize) {
8268     PetscLayout layout;
8269     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8270     PetscCall(PetscLayoutGetSize(layout, globalSize));
8271     PetscCall(PetscLayoutDestroy(&layout));
8272   }
8273   PetscCall(PetscSectionDestroy(&section));
8274   PetscCall(PetscSectionDestroy(&globalSection));
8275   PetscFunctionReturn(0);
8276 }
8277 
8278 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8279 {
8280   PetscInt cellHeight, cStart, cEnd;
8281 
8282   PetscFunctionBegin;
8283   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8284   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8285   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8286   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8287   PetscFunctionReturn(0);
8288 }
8289 
8290 /*@
8291   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8292 
8293   Input Parameter:
8294 . dm   - The `DMPLEX` object
8295 
8296   Output Parameter:
8297 . globalCellNumbers - Global cell numbers for all cells on this process
8298 
8299   Level: developer
8300 
8301 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8302 @*/
8303 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8304 {
8305   DM_Plex *mesh = (DM_Plex *)dm->data;
8306 
8307   PetscFunctionBegin;
8308   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8309   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8310   *globalCellNumbers = mesh->globalCellNumbers;
8311   PetscFunctionReturn(0);
8312 }
8313 
8314 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8315 {
8316   PetscInt vStart, vEnd;
8317 
8318   PetscFunctionBegin;
8319   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8320   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8321   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8322   PetscFunctionReturn(0);
8323 }
8324 
8325 /*@
8326   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8327 
8328   Input Parameter:
8329 . dm   - The `DMPLEX` object
8330 
8331   Output Parameter:
8332 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8333 
8334   Level: developer
8335 
8336 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8337 @*/
8338 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8339 {
8340   DM_Plex *mesh = (DM_Plex *)dm->data;
8341 
8342   PetscFunctionBegin;
8343   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8344   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8345   *globalVertexNumbers = mesh->globalVertexNumbers;
8346   PetscFunctionReturn(0);
8347 }
8348 
8349 /*@
8350   DMPlexCreatePointNumbering - Create a global numbering for all points.
8351 
8352   Collective on dm
8353 
8354   Input Parameter:
8355 . dm   - The `DMPLEX` object
8356 
8357   Output Parameter:
8358 . globalPointNumbers - Global numbers for all points on this process
8359 
8360   Level: developer
8361 
8362   Notes:
8363   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8364   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8365   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8366   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8367 
8368   The partitioned mesh is
8369 ```
8370  (2)--0--(3)--1--(4)    (1)--0--(2)
8371 ```
8372   and its global numbering is
8373 ```
8374   (3)--0--(4)--1--(5)--2--(6)
8375 ```
8376   Then the global numbering is provided as
8377 ```
8378 [0] Number of indices in set 5
8379 [0] 0 0
8380 [0] 1 1
8381 [0] 2 3
8382 [0] 3 4
8383 [0] 4 -6
8384 [1] Number of indices in set 3
8385 [1] 0 2
8386 [1] 1 5
8387 [1] 2 6
8388 ```
8389 
8390 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8391 @*/
8392 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8393 {
8394   IS        nums[4];
8395   PetscInt  depths[4], gdepths[4], starts[4];
8396   PetscInt  depth, d, shift = 0;
8397   PetscBool empty = PETSC_FALSE;
8398 
8399   PetscFunctionBegin;
8400   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8401   PetscCall(DMPlexGetDepth(dm, &depth));
8402   // For unstratified meshes use dim instead of depth
8403   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8404   // If any stratum is empty, we must mark all empty
8405   for (d = 0; d <= depth; ++d) {
8406     PetscInt end;
8407 
8408     depths[d] = depth - d;
8409     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8410     if (!(starts[d] - end)) empty = PETSC_TRUE;
8411   }
8412   if (empty)
8413     for (d = 0; d <= depth; ++d) {
8414       depths[d] = -1;
8415       starts[d] = -1;
8416     }
8417   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8418   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8419   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]);
8420   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8421   for (d = 0; d <= depth; ++d) {
8422     PetscInt pStart, pEnd, gsize;
8423 
8424     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8425     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8426     shift += gsize;
8427   }
8428   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8429   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8430   PetscFunctionReturn(0);
8431 }
8432 
8433 /*@
8434   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8435 
8436   Input Parameter:
8437 . dm - The `DMPLEX` object
8438 
8439   Output Parameter:
8440 . ranks - The rank field
8441 
8442   Options Database Key:
8443 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8444 
8445   Level: intermediate
8446 
8447 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8448 @*/
8449 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8450 {
8451   DM             rdm;
8452   PetscFE        fe;
8453   PetscScalar   *r;
8454   PetscMPIInt    rank;
8455   DMPolytopeType ct;
8456   PetscInt       dim, cStart, cEnd, c;
8457   PetscBool      simplex;
8458 
8459   PetscFunctionBeginUser;
8460   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8461   PetscValidPointer(ranks, 2);
8462   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8463   PetscCall(DMClone(dm, &rdm));
8464   PetscCall(DMGetDimension(rdm, &dim));
8465   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8466   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8467   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8468   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8469   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8470   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8471   PetscCall(PetscFEDestroy(&fe));
8472   PetscCall(DMCreateDS(rdm));
8473   PetscCall(DMCreateGlobalVector(rdm, ranks));
8474   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8475   PetscCall(VecGetArray(*ranks, &r));
8476   for (c = cStart; c < cEnd; ++c) {
8477     PetscScalar *lr;
8478 
8479     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8480     if (lr) *lr = rank;
8481   }
8482   PetscCall(VecRestoreArray(*ranks, &r));
8483   PetscCall(DMDestroy(&rdm));
8484   PetscFunctionReturn(0);
8485 }
8486 
8487 /*@
8488   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8489 
8490   Input Parameters:
8491 + dm    - The DMPlex
8492 - label - The DMLabel
8493 
8494   Output Parameter:
8495 . val - The label value field
8496 
8497   Options Database Keys:
8498 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8499 
8500   Level: intermediate
8501 
8502 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8503 @*/
8504 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8505 {
8506   DM           rdm;
8507   PetscFE      fe;
8508   PetscScalar *v;
8509   PetscInt     dim, cStart, cEnd, c;
8510 
8511   PetscFunctionBeginUser;
8512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8513   PetscValidPointer(label, 2);
8514   PetscValidPointer(val, 3);
8515   PetscCall(DMClone(dm, &rdm));
8516   PetscCall(DMGetDimension(rdm, &dim));
8517   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8518   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8519   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8520   PetscCall(PetscFEDestroy(&fe));
8521   PetscCall(DMCreateDS(rdm));
8522   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8523   PetscCall(DMCreateGlobalVector(rdm, val));
8524   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8525   PetscCall(VecGetArray(*val, &v));
8526   for (c = cStart; c < cEnd; ++c) {
8527     PetscScalar *lv;
8528     PetscInt     cval;
8529 
8530     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8531     PetscCall(DMLabelGetValue(label, c, &cval));
8532     *lv = cval;
8533   }
8534   PetscCall(VecRestoreArray(*val, &v));
8535   PetscCall(DMDestroy(&rdm));
8536   PetscFunctionReturn(0);
8537 }
8538 
8539 /*@
8540   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8541 
8542   Input Parameter:
8543 . dm - The `DMPLEX` object
8544 
8545   Level: developer
8546 
8547   Notes:
8548   This is a useful diagnostic when creating meshes programmatically.
8549 
8550   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8551 
8552 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8553 @*/
8554 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8555 {
8556   PetscSection    coneSection, supportSection;
8557   const PetscInt *cone, *support;
8558   PetscInt        coneSize, c, supportSize, s;
8559   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8560   PetscBool       storagecheck = PETSC_TRUE;
8561 
8562   PetscFunctionBegin;
8563   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8564   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8565   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8566   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8567   /* Check that point p is found in the support of its cone points, and vice versa */
8568   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8569   for (p = pStart; p < pEnd; ++p) {
8570     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8571     PetscCall(DMPlexGetCone(dm, p, &cone));
8572     for (c = 0; c < coneSize; ++c) {
8573       PetscBool dup = PETSC_FALSE;
8574       PetscInt  d;
8575       for (d = c - 1; d >= 0; --d) {
8576         if (cone[c] == cone[d]) {
8577           dup = PETSC_TRUE;
8578           break;
8579         }
8580       }
8581       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8582       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8583       for (s = 0; s < supportSize; ++s) {
8584         if (support[s] == p) break;
8585       }
8586       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8587         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8588         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8589         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8590         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8591         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8592         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8593         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]);
8594         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8595       }
8596     }
8597     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8598     if (p != pp) {
8599       storagecheck = PETSC_FALSE;
8600       continue;
8601     }
8602     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8603     PetscCall(DMPlexGetSupport(dm, p, &support));
8604     for (s = 0; s < supportSize; ++s) {
8605       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8606       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8607       for (c = 0; c < coneSize; ++c) {
8608         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8609         if (cone[c] != pp) {
8610           c = 0;
8611           break;
8612         }
8613         if (cone[c] == p) break;
8614       }
8615       if (c >= coneSize) {
8616         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8617         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8618         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8619         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8620         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8621         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8622         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8623       }
8624     }
8625   }
8626   if (storagecheck) {
8627     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8628     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8629     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8630   }
8631   PetscFunctionReturn(0);
8632 }
8633 
8634 /*
8635   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.
8636 */
8637 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8638 {
8639   DMPolytopeType  cct;
8640   PetscInt        ptpoints[4];
8641   const PetscInt *cone, *ccone, *ptcone;
8642   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8643 
8644   PetscFunctionBegin;
8645   *unsplit = 0;
8646   switch (ct) {
8647   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8648     ptpoints[npt++] = c;
8649     break;
8650   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8651     PetscCall(DMPlexGetCone(dm, c, &cone));
8652     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8653     for (cp = 0; cp < coneSize; ++cp) {
8654       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8655       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8656     }
8657     break;
8658   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8659   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8660     PetscCall(DMPlexGetCone(dm, c, &cone));
8661     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8662     for (cp = 0; cp < coneSize; ++cp) {
8663       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8664       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8665       for (ccp = 0; ccp < cconeSize; ++ccp) {
8666         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8667         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8668           PetscInt p;
8669           for (p = 0; p < npt; ++p)
8670             if (ptpoints[p] == ccone[ccp]) break;
8671           if (p == npt) ptpoints[npt++] = ccone[ccp];
8672         }
8673       }
8674     }
8675     break;
8676   default:
8677     break;
8678   }
8679   for (pt = 0; pt < npt; ++pt) {
8680     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8681     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8682   }
8683   PetscFunctionReturn(0);
8684 }
8685 
8686 /*@
8687   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8688 
8689   Input Parameters:
8690 + dm - The `DMPLEX` object
8691 - cellHeight - Normally 0
8692 
8693   Level: developer
8694 
8695   Notes:
8696   This is a useful diagnostic when creating meshes programmatically.
8697   Currently applicable only to homogeneous simplex or tensor meshes.
8698 
8699   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8700 
8701 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8702 @*/
8703 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8704 {
8705   DMPlexInterpolatedFlag interp;
8706   DMPolytopeType         ct;
8707   PetscInt               vStart, vEnd, cStart, cEnd, c;
8708 
8709   PetscFunctionBegin;
8710   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8711   PetscCall(DMPlexIsInterpolated(dm, &interp));
8712   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8713   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8714   for (c = cStart; c < cEnd; ++c) {
8715     PetscInt *closure = NULL;
8716     PetscInt  coneSize, closureSize, cl, Nv = 0;
8717 
8718     PetscCall(DMPlexGetCellType(dm, c, &ct));
8719     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8720     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8721     if (interp == DMPLEX_INTERPOLATED_FULL) {
8722       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8723       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));
8724     }
8725     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8726     for (cl = 0; cl < closureSize * 2; cl += 2) {
8727       const PetscInt p = closure[cl];
8728       if ((p >= vStart) && (p < vEnd)) ++Nv;
8729     }
8730     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8731     /* Special Case: Tensor faces with identified vertices */
8732     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8733       PetscInt unsplit;
8734 
8735       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8736       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8737     }
8738     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));
8739   }
8740   PetscFunctionReturn(0);
8741 }
8742 
8743 /*@
8744   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8745 
8746   Collective on dm
8747 
8748   Input Parameters:
8749 + dm - The `DMPLEX` object
8750 - cellHeight - Normally 0
8751 
8752   Level: developer
8753 
8754   Notes:
8755   This is a useful diagnostic when creating meshes programmatically.
8756   This routine is only relevant for meshes that are fully interpolated across all ranks.
8757   It will error out if a partially interpolated mesh is given on some rank.
8758   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8759 
8760   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8761 
8762 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8763 @*/
8764 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8765 {
8766   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8767   DMPlexInterpolatedFlag interpEnum;
8768 
8769   PetscFunctionBegin;
8770   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8771   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8772   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8773   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8774     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8775     PetscFunctionReturn(0);
8776   }
8777 
8778   PetscCall(DMGetDimension(dm, &dim));
8779   PetscCall(DMPlexGetDepth(dm, &depth));
8780   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8781   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8782     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8783     for (c = cStart; c < cEnd; ++c) {
8784       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8785       const DMPolytopeType *faceTypes;
8786       DMPolytopeType        ct;
8787       PetscInt              numFaces, coneSize, f;
8788       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8789 
8790       PetscCall(DMPlexGetCellType(dm, c, &ct));
8791       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8792       if (unsplit) continue;
8793       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8794       PetscCall(DMPlexGetCone(dm, c, &cone));
8795       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8796       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8797       for (cl = 0; cl < closureSize * 2; cl += 2) {
8798         const PetscInt p = closure[cl];
8799         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8800       }
8801       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8802       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);
8803       for (f = 0; f < numFaces; ++f) {
8804         DMPolytopeType fct;
8805         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8806 
8807         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8808         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8809         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8810           const PetscInt p = fclosure[cl];
8811           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8812         }
8813         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]);
8814         for (v = 0; v < fnumCorners; ++v) {
8815           if (fclosure[v] != faces[fOff + v]) {
8816             PetscInt v1;
8817 
8818             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8819             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8820             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8821             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8822             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8823             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]);
8824           }
8825         }
8826         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8827         fOff += faceSizes[f];
8828       }
8829       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8830       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8831     }
8832   }
8833   PetscFunctionReturn(0);
8834 }
8835 
8836 /*@
8837   DMPlexCheckGeometry - Check the geometry of mesh cells
8838 
8839   Input Parameter:
8840 . dm - The `DMPLEX` object
8841 
8842   Level: developer
8843 
8844   Notes:
8845   This is a useful diagnostic when creating meshes programmatically.
8846 
8847   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8848 
8849 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8850 @*/
8851 PetscErrorCode DMPlexCheckGeometry(DM dm)
8852 {
8853   Vec       coordinates;
8854   PetscReal detJ, J[9], refVol = 1.0;
8855   PetscReal vol;
8856   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8857 
8858   PetscFunctionBegin;
8859   PetscCall(DMGetDimension(dm, &dim));
8860   PetscCall(DMGetCoordinateDim(dm, &dE));
8861   if (dim != dE) PetscFunctionReturn(0);
8862   PetscCall(DMPlexGetDepth(dm, &depth));
8863   for (d = 0; d < dim; ++d) refVol *= 2.0;
8864   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8865   /* Make sure local coordinates are created, because that step is collective */
8866   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8867   if (!coordinates) PetscFunctionReturn(0);
8868   for (c = cStart; c < cEnd; ++c) {
8869     DMPolytopeType ct;
8870     PetscInt       unsplit;
8871     PetscBool      ignoreZeroVol = PETSC_FALSE;
8872 
8873     PetscCall(DMPlexGetCellType(dm, c, &ct));
8874     switch (ct) {
8875     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8876     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8877     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8878       ignoreZeroVol = PETSC_TRUE;
8879       break;
8880     default:
8881       break;
8882     }
8883     switch (ct) {
8884     case DM_POLYTOPE_TRI_PRISM:
8885     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8886     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8887     case DM_POLYTOPE_PYRAMID:
8888       continue;
8889     default:
8890       break;
8891     }
8892     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8893     if (unsplit) continue;
8894     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8895     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);
8896     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8897     /* This should work with periodicity since DG coordinates should be used */
8898     if (depth > 1) {
8899       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8900       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);
8901       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8902     }
8903   }
8904   PetscFunctionReturn(0);
8905 }
8906 
8907 /*@
8908   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8909 
8910   Collective on dm
8911 
8912   Input Parameters:
8913 + dm - The `DMPLEX` object
8914 . pointSF - The `PetscSF`, or NULL for `PointSF` attached to `DM`
8915 - allowExtraRoots - Flag to allow extra points not present in the `DM`
8916 
8917   Level: developer
8918 
8919   Notes:
8920   This is mainly intended for debugging/testing purposes.
8921 
8922   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8923 
8924   Extra roots can come from priodic cuts, where additional points appear on the boundary
8925 
8926 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
8927 @*/
8928 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
8929 {
8930   PetscInt           l, nleaves, nroots, overlap;
8931   const PetscInt    *locals;
8932   const PetscSFNode *remotes;
8933   PetscBool          distributed;
8934   MPI_Comm           comm;
8935   PetscMPIInt        rank;
8936 
8937   PetscFunctionBegin;
8938   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8939   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8940   else pointSF = dm->sf;
8941   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8942   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8943   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8944   {
8945     PetscMPIInt mpiFlag;
8946 
8947     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8948     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8949   }
8950   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8951   PetscCall(DMPlexIsDistributed(dm, &distributed));
8952   if (!distributed) {
8953     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);
8954     PetscFunctionReturn(0);
8955   }
8956   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);
8957   PetscCall(DMPlexGetOverlap(dm, &overlap));
8958 
8959   /* Check SF graph is compatible with DMPlex chart */
8960   {
8961     PetscInt pStart, pEnd, maxLeaf;
8962 
8963     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8964     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8965     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
8966     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8967   }
8968 
8969   /* Check Point SF has no local points referenced */
8970   for (l = 0; l < nleaves; l++) {
8971     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);
8972   }
8973 
8974   /* Check there are no cells in interface */
8975   if (!overlap) {
8976     PetscInt cellHeight, cStart, cEnd;
8977 
8978     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8979     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8980     for (l = 0; l < nleaves; ++l) {
8981       const PetscInt point = locals ? locals[l] : l;
8982 
8983       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8984     }
8985   }
8986 
8987   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8988   {
8989     const PetscInt *rootdegree;
8990 
8991     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8992     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8993     for (l = 0; l < nleaves; ++l) {
8994       const PetscInt  point = locals ? locals[l] : l;
8995       const PetscInt *cone;
8996       PetscInt        coneSize, c, idx;
8997 
8998       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8999       PetscCall(DMPlexGetCone(dm, point, &cone));
9000       for (c = 0; c < coneSize; ++c) {
9001         if (!rootdegree[cone[c]]) {
9002           if (locals) {
9003             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9004           } else {
9005             idx = (cone[c] < nleaves) ? cone[c] : -1;
9006           }
9007           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9008         }
9009       }
9010     }
9011   }
9012   PetscFunctionReturn(0);
9013 }
9014 
9015 /*@
9016   DMPlexCheck - Perform various checks of Plex sanity
9017 
9018   Input Parameter:
9019 . dm - The `DMPLEX` object
9020 
9021   Level: developer
9022 
9023   Notes:
9024   This is a useful diagnostic when creating meshes programmatically.
9025 
9026   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
9027 
9028   Currently does not include DMPlexCheckCellShape().
9029 
9030 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, DMCreate(), DMSetFromOptions()
9031 @*/
9032 PetscErrorCode DMPlexCheck(DM dm)
9033 {
9034   PetscInt cellHeight;
9035 
9036   PetscFunctionBegin;
9037   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9038   PetscCall(DMPlexCheckSymmetry(dm));
9039   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9040   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9041   PetscCall(DMPlexCheckGeometry(dm));
9042   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9043   PetscCall(DMPlexCheckInterfaceCones(dm));
9044   PetscFunctionReturn(0);
9045 }
9046 
9047 typedef struct cell_stats {
9048   PetscReal min, max, sum, squaresum;
9049   PetscInt  count;
9050 } cell_stats_t;
9051 
9052 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9053 {
9054   PetscInt i, N = *len;
9055 
9056   for (i = 0; i < N; i++) {
9057     cell_stats_t *A = (cell_stats_t *)a;
9058     cell_stats_t *B = (cell_stats_t *)b;
9059 
9060     B->min = PetscMin(A->min, B->min);
9061     B->max = PetscMax(A->max, B->max);
9062     B->sum += A->sum;
9063     B->squaresum += A->squaresum;
9064     B->count += A->count;
9065   }
9066 }
9067 
9068 /*@
9069   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9070 
9071   Collective on dm
9072 
9073   Input Parameters:
9074 + dm        - The `DMPLEX` object
9075 . output    - If true, statistics will be displayed on stdout
9076 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9077 
9078   Level: developer
9079 
9080   Notes:
9081   This is mainly intended for debugging/testing purposes.
9082 
9083   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9084 
9085 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9086 @*/
9087 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9088 {
9089   DM           dmCoarse;
9090   cell_stats_t stats, globalStats;
9091   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9092   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9093   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9094   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9095   PetscMPIInt  rank, size;
9096 
9097   PetscFunctionBegin;
9098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9099   stats.min = PETSC_MAX_REAL;
9100   stats.max = PETSC_MIN_REAL;
9101   stats.sum = stats.squaresum = 0.;
9102   stats.count                 = 0;
9103 
9104   PetscCallMPI(MPI_Comm_size(comm, &size));
9105   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9106   PetscCall(DMGetCoordinateDim(dm, &cdim));
9107   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9108   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9109   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9110   for (c = cStart; c < cEnd; c++) {
9111     PetscInt  i;
9112     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9113 
9114     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9115     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9116     for (i = 0; i < PetscSqr(cdim); ++i) {
9117       frobJ += J[i] * J[i];
9118       frobInvJ += invJ[i] * invJ[i];
9119     }
9120     cond2 = frobJ * frobInvJ;
9121     cond  = PetscSqrtReal(cond2);
9122 
9123     stats.min = PetscMin(stats.min, cond);
9124     stats.max = PetscMax(stats.max, cond);
9125     stats.sum += cond;
9126     stats.squaresum += cond2;
9127     stats.count++;
9128     if (output && cond > limit) {
9129       PetscSection coordSection;
9130       Vec          coordsLocal;
9131       PetscScalar *coords = NULL;
9132       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9133 
9134       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9135       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9136       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9137       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9138       for (i = 0; i < Nv / cdim; ++i) {
9139         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9140         for (d = 0; d < cdim; ++d) {
9141           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9142           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9143         }
9144         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9145       }
9146       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9147       for (cl = 0; cl < clSize * 2; cl += 2) {
9148         const PetscInt edge = closure[cl];
9149 
9150         if ((edge >= eStart) && (edge < eEnd)) {
9151           PetscReal len;
9152 
9153           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9154           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9155         }
9156       }
9157       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9158       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9159     }
9160   }
9161   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9162 
9163   if (size > 1) {
9164     PetscMPIInt  blockLengths[2] = {4, 1};
9165     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9166     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9167     MPI_Op       statReduce;
9168 
9169     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9170     PetscCallMPI(MPI_Type_commit(&statType));
9171     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9172     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9173     PetscCallMPI(MPI_Op_free(&statReduce));
9174     PetscCallMPI(MPI_Type_free(&statType));
9175   } else {
9176     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9177   }
9178   if (rank == 0) {
9179     count = globalStats.count;
9180     min   = globalStats.min;
9181     max   = globalStats.max;
9182     mean  = globalStats.sum / globalStats.count;
9183     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9184   }
9185 
9186   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));
9187   PetscCall(PetscFree2(J, invJ));
9188 
9189   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9190   if (dmCoarse) {
9191     PetscBool isplex;
9192 
9193     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9194     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9195   }
9196   PetscFunctionReturn(0);
9197 }
9198 
9199 /*@
9200   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9201   orthogonal quality below given tolerance.
9202 
9203   Collective on dm
9204 
9205   Input Parameters:
9206 + dm   - The `DMPLEX` object
9207 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9208 - atol - [0, 1] Absolute tolerance for tagging cells.
9209 
9210   Output Parameters:
9211 + OrthQual      - Vec containing orthogonal quality per cell
9212 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9213 
9214   Options Database Keys:
9215 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9216 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9217 
9218   Level: intermediate
9219 
9220   Notes:
9221   Orthogonal quality is given by the following formula:
9222 
9223   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9224 
9225   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
9226   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9227   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9228   calculating the cosine of the angle between these vectors.
9229 
9230   Orthogonal quality ranges from 1 (best) to 0 (worst).
9231 
9232   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9233   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9234 
9235   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9236 
9237 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9238 @*/
9239 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9240 {
9241   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9242   PetscInt              *idx;
9243   PetscScalar           *oqVals;
9244   const PetscScalar     *cellGeomArr, *faceGeomArr;
9245   PetscReal             *ci, *fi, *Ai;
9246   MPI_Comm               comm;
9247   Vec                    cellgeom, facegeom;
9248   DM                     dmFace, dmCell;
9249   IS                     glob;
9250   ISLocalToGlobalMapping ltog;
9251   PetscViewer            vwr;
9252 
9253   PetscFunctionBegin;
9254   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9255   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9256   PetscValidPointer(OrthQual, 4);
9257   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9258   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9259   PetscCall(DMGetDimension(dm, &nc));
9260   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9261   {
9262     DMPlexInterpolatedFlag interpFlag;
9263 
9264     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9265     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9266       PetscMPIInt rank;
9267 
9268       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9269       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9270     }
9271   }
9272   if (OrthQualLabel) {
9273     PetscValidPointer(OrthQualLabel, 5);
9274     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9275     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9276   } else {
9277     *OrthQualLabel = NULL;
9278   }
9279   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9280   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9281   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9282   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9283   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9284   PetscCall(VecCreate(comm, OrthQual));
9285   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9286   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9287   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9288   PetscCall(VecSetUp(*OrthQual));
9289   PetscCall(ISDestroy(&glob));
9290   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9291   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9292   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9293   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9294   PetscCall(VecGetDM(cellgeom, &dmCell));
9295   PetscCall(VecGetDM(facegeom, &dmFace));
9296   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9297   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9298     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9299     PetscInt         cellarr[2], *adj = NULL;
9300     PetscScalar     *cArr, *fArr;
9301     PetscReal        minvalc = 1.0, minvalf = 1.0;
9302     PetscFVCellGeom *cg;
9303 
9304     idx[cellIter] = cell - cStart;
9305     cellarr[0]    = cell;
9306     /* Make indexing into cellGeom easier */
9307     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9308     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9309     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9310     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9311     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9312       PetscInt         i;
9313       const PetscInt   neigh  = adj[cellneigh];
9314       PetscReal        normci = 0, normfi = 0, normai = 0;
9315       PetscFVCellGeom *cgneigh;
9316       PetscFVFaceGeom *fg;
9317 
9318       /* Don't count ourselves in the neighbor list */
9319       if (neigh == cell) continue;
9320       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9321       cellarr[1] = neigh;
9322       {
9323         PetscInt        numcovpts;
9324         const PetscInt *covpts;
9325 
9326         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9327         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9328         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9329       }
9330 
9331       /* Compute c_i, f_i and their norms */
9332       for (i = 0; i < nc; i++) {
9333         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9334         fi[i] = fg->centroid[i] - cg->centroid[i];
9335         Ai[i] = fg->normal[i];
9336         normci += PetscPowReal(ci[i], 2);
9337         normfi += PetscPowReal(fi[i], 2);
9338         normai += PetscPowReal(Ai[i], 2);
9339       }
9340       normci = PetscSqrtReal(normci);
9341       normfi = PetscSqrtReal(normfi);
9342       normai = PetscSqrtReal(normai);
9343 
9344       /* Normalize and compute for each face-cell-normal pair */
9345       for (i = 0; i < nc; i++) {
9346         ci[i] = ci[i] / normci;
9347         fi[i] = fi[i] / normfi;
9348         Ai[i] = Ai[i] / normai;
9349         /* PetscAbs because I don't know if normals are guaranteed to point out */
9350         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9351         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9352       }
9353       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9354       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9355     }
9356     PetscCall(PetscFree(adj));
9357     PetscCall(PetscFree2(cArr, fArr));
9358     /* Defer to cell if they're equal */
9359     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9360     if (OrthQualLabel) {
9361       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9362     }
9363   }
9364   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9365   PetscCall(VecAssemblyBegin(*OrthQual));
9366   PetscCall(VecAssemblyEnd(*OrthQual));
9367   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9368   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9369   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9370   if (OrthQualLabel) {
9371     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9372   }
9373   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9374   PetscCall(PetscViewerDestroy(&vwr));
9375   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9376   PetscFunctionReturn(0);
9377 }
9378 
9379 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9380  * interpolator construction */
9381 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9382 {
9383   PetscSection section, newSection, gsection;
9384   PetscSF      sf;
9385   PetscBool    hasConstraints, ghasConstraints;
9386 
9387   PetscFunctionBegin;
9388   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9389   PetscValidPointer(odm, 2);
9390   PetscCall(DMGetLocalSection(dm, &section));
9391   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9392   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9393   if (!ghasConstraints) {
9394     PetscCall(PetscObjectReference((PetscObject)dm));
9395     *odm = dm;
9396     PetscFunctionReturn(0);
9397   }
9398   PetscCall(DMClone(dm, odm));
9399   PetscCall(DMCopyFields(dm, *odm));
9400   PetscCall(DMGetLocalSection(*odm, &newSection));
9401   PetscCall(DMGetPointSF(*odm, &sf));
9402   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9403   PetscCall(DMSetGlobalSection(*odm, gsection));
9404   PetscCall(PetscSectionDestroy(&gsection));
9405   PetscFunctionReturn(0);
9406 }
9407 
9408 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9409 {
9410   DM        dmco, dmfo;
9411   Mat       interpo;
9412   Vec       rscale;
9413   Vec       cglobalo, clocal;
9414   Vec       fglobal, fglobalo, flocal;
9415   PetscBool regular;
9416 
9417   PetscFunctionBegin;
9418   PetscCall(DMGetFullDM(dmc, &dmco));
9419   PetscCall(DMGetFullDM(dmf, &dmfo));
9420   PetscCall(DMSetCoarseDM(dmfo, dmco));
9421   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9422   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9423   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9424   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9425   PetscCall(DMCreateLocalVector(dmc, &clocal));
9426   PetscCall(VecSet(cglobalo, 0.));
9427   PetscCall(VecSet(clocal, 0.));
9428   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9429   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9430   PetscCall(DMCreateLocalVector(dmf, &flocal));
9431   PetscCall(VecSet(fglobal, 0.));
9432   PetscCall(VecSet(fglobalo, 0.));
9433   PetscCall(VecSet(flocal, 0.));
9434   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9435   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9436   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9437   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9438   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9439   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9440   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9441   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9442   *shift = fglobal;
9443   PetscCall(VecDestroy(&flocal));
9444   PetscCall(VecDestroy(&fglobalo));
9445   PetscCall(VecDestroy(&clocal));
9446   PetscCall(VecDestroy(&cglobalo));
9447   PetscCall(VecDestroy(&rscale));
9448   PetscCall(MatDestroy(&interpo));
9449   PetscCall(DMDestroy(&dmfo));
9450   PetscCall(DMDestroy(&dmco));
9451   PetscFunctionReturn(0);
9452 }
9453 
9454 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9455 {
9456   PetscObject shifto;
9457   Vec         shift;
9458 
9459   PetscFunctionBegin;
9460   if (!interp) {
9461     Vec rscale;
9462 
9463     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9464     PetscCall(VecDestroy(&rscale));
9465   } else {
9466     PetscCall(PetscObjectReference((PetscObject)interp));
9467   }
9468   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9469   if (!shifto) {
9470     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9471     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9472     shifto = (PetscObject)shift;
9473     PetscCall(VecDestroy(&shift));
9474   }
9475   shift = (Vec)shifto;
9476   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9477   PetscCall(VecAXPY(fineSol, 1.0, shift));
9478   PetscCall(MatDestroy(&interp));
9479   PetscFunctionReturn(0);
9480 }
9481 
9482 /* Pointwise interpolation
9483      Just code FEM for now
9484      u^f = I u^c
9485      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9486      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9487      I_{ij} = psi^f_i phi^c_j
9488 */
9489 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9490 {
9491   PetscSection gsc, gsf;
9492   PetscInt     m, n;
9493   void        *ctx;
9494   DM           cdm;
9495   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9496 
9497   PetscFunctionBegin;
9498   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9499   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9500   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9501   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9502 
9503   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9504   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9505   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9506   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9507   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9508 
9509   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9510   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9511   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9512   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9513   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9514   if (scaling) {
9515     /* Use naive scaling */
9516     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9517   }
9518   PetscFunctionReturn(0);
9519 }
9520 
9521 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9522 {
9523   VecScatter ctx;
9524 
9525   PetscFunctionBegin;
9526   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9527   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9528   PetscCall(VecScatterDestroy(&ctx));
9529   PetscFunctionReturn(0);
9530 }
9531 
9532 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[])
9533 {
9534   const PetscInt Nc = uOff[1] - uOff[0];
9535   PetscInt       c;
9536   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9537 }
9538 
9539 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9540 {
9541   DM           dmc;
9542   PetscDS      ds;
9543   Vec          ones, locmass;
9544   IS           cellIS;
9545   PetscFormKey key;
9546   PetscInt     depth;
9547 
9548   PetscFunctionBegin;
9549   PetscCall(DMClone(dm, &dmc));
9550   PetscCall(DMCopyDisc(dm, dmc));
9551   PetscCall(DMGetDS(dmc, &ds));
9552   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9553   PetscCall(DMCreateGlobalVector(dmc, mass));
9554   PetscCall(DMGetLocalVector(dmc, &ones));
9555   PetscCall(DMGetLocalVector(dmc, &locmass));
9556   PetscCall(DMPlexGetDepth(dmc, &depth));
9557   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9558   PetscCall(VecSet(locmass, 0.0));
9559   PetscCall(VecSet(ones, 1.0));
9560   key.label = NULL;
9561   key.value = 0;
9562   key.field = 0;
9563   key.part  = 0;
9564   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9565   PetscCall(ISDestroy(&cellIS));
9566   PetscCall(VecSet(*mass, 0.0));
9567   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9568   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9569   PetscCall(DMRestoreLocalVector(dmc, &ones));
9570   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9571   PetscCall(DMDestroy(&dmc));
9572   PetscFunctionReturn(0);
9573 }
9574 
9575 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9576 {
9577   PetscSection gsc, gsf;
9578   PetscInt     m, n;
9579   void        *ctx;
9580   DM           cdm;
9581   PetscBool    regular;
9582 
9583   PetscFunctionBegin;
9584   if (dmFine == dmCoarse) {
9585     DM            dmc;
9586     PetscDS       ds;
9587     PetscWeakForm wf;
9588     Vec           u;
9589     IS            cellIS;
9590     PetscFormKey  key;
9591     PetscInt      depth;
9592 
9593     PetscCall(DMClone(dmFine, &dmc));
9594     PetscCall(DMCopyDisc(dmFine, dmc));
9595     PetscCall(DMGetDS(dmc, &ds));
9596     PetscCall(PetscDSGetWeakForm(ds, &wf));
9597     PetscCall(PetscWeakFormClear(wf));
9598     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9599     PetscCall(DMCreateMatrix(dmc, mass));
9600     PetscCall(DMGetLocalVector(dmc, &u));
9601     PetscCall(DMPlexGetDepth(dmc, &depth));
9602     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9603     PetscCall(MatZeroEntries(*mass));
9604     key.label = NULL;
9605     key.value = 0;
9606     key.field = 0;
9607     key.part  = 0;
9608     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9609     PetscCall(ISDestroy(&cellIS));
9610     PetscCall(DMRestoreLocalVector(dmc, &u));
9611     PetscCall(DMDestroy(&dmc));
9612   } else {
9613     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9614     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9615     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9616     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9617 
9618     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9619     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9620     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9621     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9622 
9623     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9624     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9625     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9626     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9627   }
9628   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9629   PetscFunctionReturn(0);
9630 }
9631 
9632 /*@
9633   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9634 
9635   Input Parameter:
9636 . dm - The `DMPLEX` object
9637 
9638   Output Parameter:
9639 . regular - The flag
9640 
9641   Level: intermediate
9642 
9643 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9644 @*/
9645 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9646 {
9647   PetscFunctionBegin;
9648   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9649   PetscValidBoolPointer(regular, 2);
9650   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9651   PetscFunctionReturn(0);
9652 }
9653 
9654 /*@
9655   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9656 
9657   Input Parameters:
9658 + dm - The `DMPLEX` object
9659 - regular - The flag
9660 
9661   Level: intermediate
9662 
9663 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9664 @*/
9665 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9666 {
9667   PetscFunctionBegin;
9668   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9669   ((DM_Plex *)dm->data)->regularRefinement = regular;
9670   PetscFunctionReturn(0);
9671 }
9672 
9673 /*@
9674   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9675   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9676 
9677   Not Collective
9678 
9679   Input Parameter:
9680 . dm - The `DMPLEX` object
9681 
9682   Output Parameters:
9683 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9684 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9685 
9686   Level: intermediate
9687 
9688 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9689 @*/
9690 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9691 {
9692   DM_Plex *plex = (DM_Plex *)dm->data;
9693 
9694   PetscFunctionBegin;
9695   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9696   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9697   if (anchorSection) *anchorSection = plex->anchorSection;
9698   if (anchorIS) *anchorIS = plex->anchorIS;
9699   PetscFunctionReturn(0);
9700 }
9701 
9702 /*@
9703   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9704   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9705   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9706 
9707   Collective on dm
9708 
9709   Input Parameters:
9710 + dm - The `DMPLEX` object
9711 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9712                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9713 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9714 
9715   Level: intermediate
9716 
9717   Notes:
9718   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9719   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9720 
9721   The reference counts of anchorSection and anchorIS are incremented.
9722 
9723 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9724 @*/
9725 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9726 {
9727   DM_Plex    *plex = (DM_Plex *)dm->data;
9728   PetscMPIInt result;
9729 
9730   PetscFunctionBegin;
9731   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9732   if (anchorSection) {
9733     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9734     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9735     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9736   }
9737   if (anchorIS) {
9738     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9739     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9740     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9741   }
9742 
9743   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9744   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9745   plex->anchorSection = anchorSection;
9746 
9747   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9748   PetscCall(ISDestroy(&plex->anchorIS));
9749   plex->anchorIS = anchorIS;
9750 
9751   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9752     PetscInt        size, a, pStart, pEnd;
9753     const PetscInt *anchors;
9754 
9755     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9756     PetscCall(ISGetLocalSize(anchorIS, &size));
9757     PetscCall(ISGetIndices(anchorIS, &anchors));
9758     for (a = 0; a < size; a++) {
9759       PetscInt p;
9760 
9761       p = anchors[a];
9762       if (p >= pStart && p < pEnd) {
9763         PetscInt dof;
9764 
9765         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9766         if (dof) {
9767           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9768           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9769         }
9770       }
9771     }
9772     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9773   }
9774   /* reset the generic constraints */
9775   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9776   PetscFunctionReturn(0);
9777 }
9778 
9779 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9780 {
9781   PetscSection anchorSection;
9782   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9783 
9784   PetscFunctionBegin;
9785   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9786   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9787   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9788   PetscCall(PetscSectionGetNumFields(section, &numFields));
9789   if (numFields) {
9790     PetscInt f;
9791     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9792 
9793     for (f = 0; f < numFields; f++) {
9794       PetscInt numComp;
9795 
9796       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9797       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9798     }
9799   }
9800   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9801   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9802   pStart = PetscMax(pStart, sStart);
9803   pEnd   = PetscMin(pEnd, sEnd);
9804   pEnd   = PetscMax(pStart, pEnd);
9805   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9806   for (p = pStart; p < pEnd; p++) {
9807     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9808     if (dof) {
9809       PetscCall(PetscSectionGetDof(section, p, &dof));
9810       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9811       for (f = 0; f < numFields; f++) {
9812         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9813         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9814       }
9815     }
9816   }
9817   PetscCall(PetscSectionSetUp(*cSec));
9818   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9819   PetscFunctionReturn(0);
9820 }
9821 
9822 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9823 {
9824   PetscSection    aSec;
9825   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9826   const PetscInt *anchors;
9827   PetscInt        numFields, f;
9828   IS              aIS;
9829   MatType         mtype;
9830   PetscBool       iscuda, iskokkos;
9831 
9832   PetscFunctionBegin;
9833   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9834   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9835   PetscCall(PetscSectionGetStorageSize(section, &n));
9836   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9837   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9838   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9839   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9840   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9841   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9842   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9843   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9844   else mtype = MATSEQAIJ;
9845   PetscCall(MatSetType(*cMat, mtype));
9846   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9847   PetscCall(ISGetIndices(aIS, &anchors));
9848   /* cSec will be a subset of aSec and section */
9849   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9850   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9851   PetscCall(PetscMalloc1(m + 1, &i));
9852   i[0] = 0;
9853   PetscCall(PetscSectionGetNumFields(section, &numFields));
9854   for (p = pStart; p < pEnd; p++) {
9855     PetscInt rDof, rOff, r;
9856 
9857     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9858     if (!rDof) continue;
9859     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9860     if (numFields) {
9861       for (f = 0; f < numFields; f++) {
9862         annz = 0;
9863         for (r = 0; r < rDof; r++) {
9864           a = anchors[rOff + r];
9865           if (a < sStart || a >= sEnd) continue;
9866           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9867           annz += aDof;
9868         }
9869         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9870         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9871         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9872       }
9873     } else {
9874       annz = 0;
9875       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9876       for (q = 0; q < dof; q++) {
9877         a = anchors[rOff + q];
9878         if (a < sStart || a >= sEnd) continue;
9879         PetscCall(PetscSectionGetDof(section, a, &aDof));
9880         annz += aDof;
9881       }
9882       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9883       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9884       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9885     }
9886   }
9887   nnz = i[m];
9888   PetscCall(PetscMalloc1(nnz, &j));
9889   offset = 0;
9890   for (p = pStart; p < pEnd; p++) {
9891     if (numFields) {
9892       for (f = 0; f < numFields; f++) {
9893         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9894         for (q = 0; q < dof; q++) {
9895           PetscInt rDof, rOff, r;
9896           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9897           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9898           for (r = 0; r < rDof; r++) {
9899             PetscInt s;
9900 
9901             a = anchors[rOff + r];
9902             if (a < sStart || a >= sEnd) continue;
9903             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9904             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9905             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9906           }
9907         }
9908       }
9909     } else {
9910       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9911       for (q = 0; q < dof; q++) {
9912         PetscInt rDof, rOff, r;
9913         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9914         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9915         for (r = 0; r < rDof; r++) {
9916           PetscInt s;
9917 
9918           a = anchors[rOff + r];
9919           if (a < sStart || a >= sEnd) continue;
9920           PetscCall(PetscSectionGetDof(section, a, &aDof));
9921           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9922           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9923         }
9924       }
9925     }
9926   }
9927   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9928   PetscCall(PetscFree(i));
9929   PetscCall(PetscFree(j));
9930   PetscCall(ISRestoreIndices(aIS, &anchors));
9931   PetscFunctionReturn(0);
9932 }
9933 
9934 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9935 {
9936   DM_Plex     *plex = (DM_Plex *)dm->data;
9937   PetscSection anchorSection, section, cSec;
9938   Mat          cMat;
9939 
9940   PetscFunctionBegin;
9941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9942   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9943   if (anchorSection) {
9944     PetscInt Nf;
9945 
9946     PetscCall(DMGetLocalSection(dm, &section));
9947     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9948     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9949     PetscCall(DMGetNumFields(dm, &Nf));
9950     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9951     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9952     PetscCall(PetscSectionDestroy(&cSec));
9953     PetscCall(MatDestroy(&cMat));
9954   }
9955   PetscFunctionReturn(0);
9956 }
9957 
9958 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9959 {
9960   IS           subis;
9961   PetscSection section, subsection;
9962 
9963   PetscFunctionBegin;
9964   PetscCall(DMGetLocalSection(dm, &section));
9965   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9966   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9967   /* Create subdomain */
9968   PetscCall(DMPlexFilter(dm, label, value, subdm));
9969   /* Create submodel */
9970   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9971   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9972   PetscCall(DMSetLocalSection(*subdm, subsection));
9973   PetscCall(PetscSectionDestroy(&subsection));
9974   PetscCall(DMCopyDisc(dm, *subdm));
9975   /* Create map from submodel to global model */
9976   if (is) {
9977     PetscSection    sectionGlobal, subsectionGlobal;
9978     IS              spIS;
9979     const PetscInt *spmap;
9980     PetscInt       *subIndices;
9981     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9982     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9983 
9984     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9985     PetscCall(ISGetIndices(spIS, &spmap));
9986     PetscCall(PetscSectionGetNumFields(section, &Nf));
9987     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9988     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9989     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9990     for (p = pStart; p < pEnd; ++p) {
9991       PetscInt gdof, pSubSize = 0;
9992 
9993       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9994       if (gdof > 0) {
9995         for (f = 0; f < Nf; ++f) {
9996           PetscInt fdof, fcdof;
9997 
9998           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9999           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10000           pSubSize += fdof - fcdof;
10001         }
10002         subSize += pSubSize;
10003         if (pSubSize) {
10004           if (bs < 0) {
10005             bs = pSubSize;
10006           } else if (bs != pSubSize) {
10007             /* Layout does not admit a pointwise block size */
10008             bs = 1;
10009           }
10010         }
10011       }
10012     }
10013     /* Must have same blocksize on all procs (some might have no points) */
10014     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10015     bsLocal[1] = bs;
10016     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10017     if (bsMinMax[0] != bsMinMax[1]) {
10018       bs = 1;
10019     } else {
10020       bs = bsMinMax[0];
10021     }
10022     PetscCall(PetscMalloc1(subSize, &subIndices));
10023     for (p = pStart; p < pEnd; ++p) {
10024       PetscInt gdof, goff;
10025 
10026       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10027       if (gdof > 0) {
10028         const PetscInt point = spmap[p];
10029 
10030         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10031         for (f = 0; f < Nf; ++f) {
10032           PetscInt fdof, fcdof, fc, f2, poff = 0;
10033 
10034           /* Can get rid of this loop by storing field information in the global section */
10035           for (f2 = 0; f2 < f; ++f2) {
10036             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10037             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10038             poff += fdof - fcdof;
10039           }
10040           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10041           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10042           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10043         }
10044       }
10045     }
10046     PetscCall(ISRestoreIndices(spIS, &spmap));
10047     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10048     if (bs > 1) {
10049       /* We need to check that the block size does not come from non-contiguous fields */
10050       PetscInt i, j, set = 1;
10051       for (i = 0; i < subSize; i += bs) {
10052         for (j = 0; j < bs; ++j) {
10053           if (subIndices[i + j] != subIndices[i] + j) {
10054             set = 0;
10055             break;
10056           }
10057         }
10058       }
10059       if (set) PetscCall(ISSetBlockSize(*is, bs));
10060     }
10061     /* Attach nullspace */
10062     for (f = 0; f < Nf; ++f) {
10063       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10064       if ((*subdm)->nullspaceConstructors[f]) break;
10065     }
10066     if (f < Nf) {
10067       MatNullSpace nullSpace;
10068       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10069 
10070       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10071       PetscCall(MatNullSpaceDestroy(&nullSpace));
10072     }
10073   }
10074   PetscFunctionReturn(0);
10075 }
10076 
10077 /*@
10078   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10079 
10080   Input Parameters:
10081 + dm - The `DM`
10082 - dummy - unused argument
10083 
10084   Options Database Key:
10085 . -dm_plex_monitor_throughput - Activate the monitor
10086 
10087   Level: developer
10088 
10089 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10090 @*/
10091 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10092 {
10093 #if defined(PETSC_USE_LOG)
10094   PetscStageLog      stageLog;
10095   PetscLogEvent      event;
10096   PetscLogStage      stage;
10097   PetscEventPerfInfo eventInfo;
10098   PetscReal          cellRate, flopRate;
10099   PetscInt           cStart, cEnd, Nf, N;
10100   const char        *name;
10101 #endif
10102 
10103   PetscFunctionBegin;
10104   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10105 #if defined(PETSC_USE_LOG)
10106   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10107   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10108   PetscCall(DMGetNumFields(dm, &Nf));
10109   PetscCall(PetscLogGetStageLog(&stageLog));
10110   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10111   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10112   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10113   N        = (cEnd - cStart) * Nf * eventInfo.count;
10114   flopRate = eventInfo.flops / eventInfo.time;
10115   cellRate = N / eventInfo.time;
10116   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)));
10117 #else
10118   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10119 #endif
10120   PetscFunctionReturn(0);
10121 }
10122