xref: /petsc/src/dm/impls/plex/plex.c (revision 993a5519d2599946bd2b7350df0aa06651d780e2)
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   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
28   If the mesh has no cells, this returns PETSC_FALSE.
29 
30   Level: intermediate
31 
32 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
33 @*/
34 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex) {
35   DMPolytopeType ct;
36   PetscInt       cStart, cEnd;
37 
38   PetscFunctionBegin;
39   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
40   if (cEnd <= cStart) {
41     *simplex = PETSC_FALSE;
42     PetscFunctionReturn(0);
43   }
44   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
45   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
46   PetscFunctionReturn(0);
47 }
48 
49 /*@
50   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
51 
52   Input Parameters:
53 + dm     - The DMPlex object
54 - height - The cell height in the Plex, 0 is the default
55 
56   Output Parameters:
57 + cStart - The first "normal" cell
58 - cEnd   - The upper bound on "normal"" cells
59 
60   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
61 
62   Level: developer
63 
64 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
65 @*/
66 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd) {
67   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
68   PetscInt       cS, cE, c;
69 
70   PetscFunctionBegin;
71   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
72   for (c = cS; c < cE; ++c) {
73     DMPolytopeType cct;
74 
75     PetscCall(DMPlexGetCellType(dm, c, &cct));
76     if ((PetscInt)cct < 0) break;
77     switch (cct) {
78     case DM_POLYTOPE_POINT:
79     case DM_POLYTOPE_SEGMENT:
80     case DM_POLYTOPE_TRIANGLE:
81     case DM_POLYTOPE_QUADRILATERAL:
82     case DM_POLYTOPE_TETRAHEDRON:
83     case DM_POLYTOPE_HEXAHEDRON: ct = cct; break;
84     default: break;
85     }
86     if (ct != DM_POLYTOPE_UNKNOWN) break;
87   }
88   if (ct != DM_POLYTOPE_UNKNOWN) {
89     DMLabel ctLabel;
90 
91     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
92     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
93     // Reset label for fast lookup
94     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
95   }
96   if (cStart) *cStart = cS;
97   if (cEnd) *cEnd = cE;
98   PetscFunctionReturn(0);
99 }
100 
101 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft) {
102   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
103   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
104 
105   PetscFunctionBegin;
106   *ft = PETSC_VTK_INVALID;
107   PetscCall(DMGetCoordinateDim(dm, &cdim));
108   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
109   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
110   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
111   if (field >= 0) {
112     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
113     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
114   } else {
115     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
116     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
117   }
118   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
119   if (globalvcdof[0]) {
120     *sStart = vStart;
121     *sEnd   = vEnd;
122     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
123     else *ft = PETSC_VTK_POINT_FIELD;
124   } else if (globalvcdof[1]) {
125     *sStart = cStart;
126     *sEnd   = cEnd;
127     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
128     else *ft = PETSC_VTK_CELL_FIELD;
129   } else {
130     if (field >= 0) {
131       const char *fieldname;
132 
133       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
134       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
135     } else {
136       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
137     }
138   }
139   PetscFunctionReturn(0);
140 }
141 
142 /*@
143   DMPlexVecView1D - Plot many 1D solutions on the same line graph
144 
145   Collective on dm
146 
147   Input Parameters:
148 + dm - The DMPlex
149 . n  - The number of vectors
150 . u  - The array of local vectors
151 - viewer - The Draw viewer
152 
153   Level: advanced
154 
155 .seealso: `VecViewFromOptions()`, `VecView()`
156 @*/
157 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer) {
158   PetscDS            ds;
159   PetscDraw          draw = NULL;
160   PetscDrawLG        lg;
161   Vec                coordinates;
162   const PetscScalar *coords, **sol;
163   PetscReal         *vals;
164   PetscInt          *Nc;
165   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
166   char             **names;
167 
168   PetscFunctionBegin;
169   PetscCall(DMGetDS(dm, &ds));
170   PetscCall(PetscDSGetNumFields(ds, &Nf));
171   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
172   PetscCall(PetscDSGetComponents(ds, &Nc));
173 
174   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
175   if (!draw) PetscFunctionReturn(0);
176   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
177 
178   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
179   for (i = 0, l = 0; i < n; ++i) {
180     const char *vname;
181 
182     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
183     for (f = 0; f < Nf; ++f) {
184       PetscObject disc;
185       const char *fname;
186       char        tmpname[PETSC_MAX_PATH_LEN];
187 
188       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
189       /* TODO Create names for components */
190       for (c = 0; c < Nc[f]; ++c, ++l) {
191         PetscCall(PetscObjectGetName(disc, &fname));
192         PetscCall(PetscStrcpy(tmpname, vname));
193         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
194         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
195         PetscCall(PetscStrallocpy(tmpname, &names[l]));
196       }
197     }
198   }
199   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
200   /* Just add P_1 support for now */
201   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
202   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
203   PetscCall(VecGetArrayRead(coordinates, &coords));
204   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
205   for (v = vStart; v < vEnd; ++v) {
206     PetscScalar *x, *svals;
207 
208     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
209     for (i = 0; i < n; ++i) {
210       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
211       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
212     }
213     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
214   }
215   PetscCall(VecRestoreArrayRead(coordinates, &coords));
216   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
217   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
218   PetscCall(PetscFree3(sol, names, vals));
219 
220   PetscCall(PetscDrawLGDraw(lg));
221   PetscCall(PetscDrawLGDestroy(&lg));
222   PetscFunctionReturn(0);
223 }
224 
225 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer) {
226   DM dm;
227 
228   PetscFunctionBegin;
229   PetscCall(VecGetDM(u, &dm));
230   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
231   PetscFunctionReturn(0);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer) {
235   DM                 dm;
236   PetscSection       s;
237   PetscDraw          draw, popup;
238   DM                 cdm;
239   PetscSection       coordSection;
240   Vec                coordinates;
241   const PetscScalar *coords, *array;
242   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
243   PetscReal          vbound[2], time;
244   PetscBool          flg;
245   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
246   const char        *name;
247   char               title[PETSC_MAX_PATH_LEN];
248 
249   PetscFunctionBegin;
250   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
251   PetscCall(VecGetDM(v, &dm));
252   PetscCall(DMGetCoordinateDim(dm, &dim));
253   PetscCall(DMGetLocalSection(dm, &s));
254   PetscCall(PetscSectionGetNumFields(s, &Nf));
255   PetscCall(DMGetCoarsenLevel(dm, &level));
256   PetscCall(DMGetCoordinateDM(dm, &cdm));
257   PetscCall(DMGetLocalSection(cdm, &coordSection));
258   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
259   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
260   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
261 
262   PetscCall(PetscObjectGetName((PetscObject)v, &name));
263   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
264 
265   PetscCall(VecGetLocalSize(coordinates, &N));
266   PetscCall(VecGetArrayRead(coordinates, &coords));
267   for (c = 0; c < N; c += dim) {
268     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
269     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
270     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
271     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
272   }
273   PetscCall(VecRestoreArrayRead(coordinates, &coords));
274   PetscCall(PetscDrawClear(draw));
275 
276   /* Could implement something like DMDASelectFields() */
277   for (f = 0; f < Nf; ++f) {
278     DM          fdm = dm;
279     Vec         fv  = v;
280     IS          fis;
281     char        prefix[PETSC_MAX_PATH_LEN];
282     const char *fname;
283 
284     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
285     PetscCall(PetscSectionGetFieldName(s, f, &fname));
286 
287     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
288     else prefix[0] = '\0';
289     if (Nf > 1) {
290       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
291       PetscCall(VecGetSubVector(v, fis, &fv));
292       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
293       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
294     }
295     for (comp = 0; comp < Nc; ++comp, ++w) {
296       PetscInt nmax = 2;
297 
298       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
299       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
300       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
301       PetscCall(PetscDrawSetTitle(draw, title));
302 
303       /* TODO Get max and min only for this component */
304       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
305       if (!flg) {
306         PetscCall(VecMin(fv, NULL, &vbound[0]));
307         PetscCall(VecMax(fv, NULL, &vbound[1]));
308         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
309       }
310       PetscCall(PetscDrawGetPopup(draw, &popup));
311       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
312       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
313 
314       PetscCall(VecGetArrayRead(fv, &array));
315       for (c = cStart; c < cEnd; ++c) {
316         PetscScalar *coords = NULL, *a   = NULL;
317         PetscInt     numCoords, color[4] = {-1, -1, -1, -1};
318 
319         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
320         if (a) {
321           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
322           color[1] = color[2] = color[3] = color[0];
323         } else {
324           PetscScalar *vals = NULL;
325           PetscInt     numVals, va;
326 
327           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
328           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);
329           switch (numVals / Nc) {
330           case 3: /* P1 Triangle */
331           case 4: /* P1 Quadrangle */
332             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
333             break;
334           case 6: /* P2 Triangle */
335           case 8: /* P2 Quadrangle */
336             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
337             break;
338           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
339           }
340           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
341         }
342         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
343         switch (numCoords) {
344         case 6:
345         case 12: /* Localized triangle */
346           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]));
347           break;
348         case 8:
349         case 16: /* Localized quadrilateral */
350           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]));
351           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]));
352           break;
353         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
354         }
355         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
356       }
357       PetscCall(VecRestoreArrayRead(fv, &array));
358       PetscCall(PetscDrawFlush(draw));
359       PetscCall(PetscDrawPause(draw));
360       PetscCall(PetscDrawSave(draw));
361     }
362     if (Nf > 1) {
363       PetscCall(VecRestoreSubVector(v, fis, &fv));
364       PetscCall(ISDestroy(&fis));
365       PetscCall(DMDestroy(&fdm));
366     }
367   }
368   PetscFunctionReturn(0);
369 }
370 
371 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer) {
372   DM        dm;
373   PetscDraw draw;
374   PetscInt  dim;
375   PetscBool isnull;
376 
377   PetscFunctionBegin;
378   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
379   PetscCall(PetscDrawIsNull(draw, &isnull));
380   if (isnull) PetscFunctionReturn(0);
381 
382   PetscCall(VecGetDM(v, &dm));
383   PetscCall(DMGetCoordinateDim(dm, &dim));
384   switch (dim) {
385   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer)); break;
386   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer)); break;
387   default: SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
388   }
389   PetscFunctionReturn(0);
390 }
391 
392 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer) {
393   DM                      dm;
394   Vec                     locv;
395   const char             *name;
396   PetscSection            section;
397   PetscInt                pStart, pEnd;
398   PetscInt                numFields;
399   PetscViewerVTKFieldType ft;
400 
401   PetscFunctionBegin;
402   PetscCall(VecGetDM(v, &dm));
403   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
404   PetscCall(PetscObjectGetName((PetscObject)v, &name));
405   PetscCall(PetscObjectSetName((PetscObject)locv, name));
406   PetscCall(VecCopy(v, locv));
407   PetscCall(DMGetLocalSection(dm, &section));
408   PetscCall(PetscSectionGetNumFields(section, &numFields));
409   if (!numFields) {
410     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
411     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
412   } else {
413     PetscInt f;
414 
415     for (f = 0; f < numFields; f++) {
416       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
417       if (ft == PETSC_VTK_INVALID) continue;
418       PetscCall(PetscObjectReference((PetscObject)locv));
419       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
420     }
421     PetscCall(VecDestroy(&locv));
422   }
423   PetscFunctionReturn(0);
424 }
425 
426 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer) {
427   DM        dm;
428   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
429 
430   PetscFunctionBegin;
431   PetscCall(VecGetDM(v, &dm));
432   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
433   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
434   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
435   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
436   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
437   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
438   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
439     PetscInt    i, numFields;
440     PetscObject fe;
441     PetscBool   fem  = PETSC_FALSE;
442     Vec         locv = v;
443     const char *name;
444     PetscInt    step;
445     PetscReal   time;
446 
447     PetscCall(DMGetNumFields(dm, &numFields));
448     for (i = 0; i < numFields; i++) {
449       PetscCall(DMGetField(dm, i, NULL, &fe));
450       if (fe->classid == PETSCFE_CLASSID) {
451         fem = PETSC_TRUE;
452         break;
453       }
454     }
455     if (fem) {
456       PetscObject isZero;
457 
458       PetscCall(DMGetLocalVector(dm, &locv));
459       PetscCall(PetscObjectGetName((PetscObject)v, &name));
460       PetscCall(PetscObjectSetName((PetscObject)locv, name));
461       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
462       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
463       PetscCall(VecCopy(v, locv));
464       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
465       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
466     }
467     if (isvtk) {
468       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
469     } else if (ishdf5) {
470 #if defined(PETSC_HAVE_HDF5)
471       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
472 #else
473       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
474 #endif
475     } else if (isdraw) {
476       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
477     } else if (isglvis) {
478       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
479       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
480       PetscCall(VecView_GLVis(locv, viewer));
481     } else if (iscgns) {
482 #if defined(PETSC_HAVE_CGNS)
483       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
484 #else
485       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
486 #endif
487     }
488     if (fem) {
489       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
490       PetscCall(DMRestoreLocalVector(dm, &locv));
491     }
492   } else {
493     PetscBool isseq;
494 
495     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
496     if (isseq) PetscCall(VecView_Seq(v, viewer));
497     else PetscCall(VecView_MPI(v, viewer));
498   }
499   PetscFunctionReturn(0);
500 }
501 
502 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer) {
503   DM        dm;
504   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
505 
506   PetscFunctionBegin;
507   PetscCall(VecGetDM(v, &dm));
508   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
509   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
510   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
511   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
512   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
513   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
514   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
515   if (isvtk || isdraw || isglvis || iscgns) {
516     Vec         locv;
517     PetscObject isZero;
518     const char *name;
519 
520     PetscCall(DMGetLocalVector(dm, &locv));
521     PetscCall(PetscObjectGetName((PetscObject)v, &name));
522     PetscCall(PetscObjectSetName((PetscObject)locv, name));
523     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
524     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
525     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
526     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
527     PetscCall(VecView_Plex_Local(locv, viewer));
528     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
529     PetscCall(DMRestoreLocalVector(dm, &locv));
530   } else if (ishdf5) {
531 #if defined(PETSC_HAVE_HDF5)
532     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
533 #else
534     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
535 #endif
536   } else if (isexodusii) {
537 #if defined(PETSC_HAVE_EXODUSII)
538     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
539 #else
540     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
541 #endif
542   } else {
543     PetscBool isseq;
544 
545     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
546     if (isseq) PetscCall(VecView_Seq(v, viewer));
547     else PetscCall(VecView_MPI(v, viewer));
548   }
549   PetscFunctionReturn(0);
550 }
551 
552 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer) {
553   DM                dm;
554   MPI_Comm          comm;
555   PetscViewerFormat format;
556   Vec               v;
557   PetscBool         isvtk, ishdf5;
558 
559   PetscFunctionBegin;
560   PetscCall(VecGetDM(originalv, &dm));
561   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
562   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
563   PetscCall(PetscViewerGetFormat(viewer, &format));
564   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
565   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
566   if (format == PETSC_VIEWER_NATIVE) {
567     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
568     /* this need a better fix */
569     if (dm->useNatural) {
570       if (dm->sfNatural) {
571         const char *vecname;
572         PetscInt    n, nroots;
573 
574         PetscCall(VecGetLocalSize(originalv, &n));
575         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
576         if (n == nroots) {
577           PetscCall(DMGetGlobalVector(dm, &v));
578           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
579           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
580           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
581           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
582         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
583       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
584     } else v = originalv;
585   } else v = originalv;
586 
587   if (ishdf5) {
588 #if defined(PETSC_HAVE_HDF5)
589     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
590 #else
591     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
592 #endif
593   } else if (isvtk) {
594     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
595   } else {
596     PetscBool isseq;
597 
598     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
599     if (isseq) PetscCall(VecView_Seq(v, viewer));
600     else PetscCall(VecView_MPI(v, viewer));
601   }
602   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
603   PetscFunctionReturn(0);
604 }
605 
606 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer) {
607   DM        dm;
608   PetscBool ishdf5;
609 
610   PetscFunctionBegin;
611   PetscCall(VecGetDM(v, &dm));
612   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
613   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
614   if (ishdf5) {
615     DM          dmBC;
616     Vec         gv;
617     const char *name;
618 
619     PetscCall(DMGetOutputDM(dm, &dmBC));
620     PetscCall(DMGetGlobalVector(dmBC, &gv));
621     PetscCall(PetscObjectGetName((PetscObject)v, &name));
622     PetscCall(PetscObjectSetName((PetscObject)gv, name));
623     PetscCall(VecLoad_Default(gv, viewer));
624     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
625     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
626     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
627   } else PetscCall(VecLoad_Default(v, viewer));
628   PetscFunctionReturn(0);
629 }
630 
631 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer) {
632   DM        dm;
633   PetscBool ishdf5, isexodusii;
634 
635   PetscFunctionBegin;
636   PetscCall(VecGetDM(v, &dm));
637   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
640   if (ishdf5) {
641 #if defined(PETSC_HAVE_HDF5)
642     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
643 #else
644     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
645 #endif
646   } else if (isexodusii) {
647 #if defined(PETSC_HAVE_EXODUSII)
648     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
649 #else
650     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
651 #endif
652   } else PetscCall(VecLoad_Default(v, viewer));
653   PetscFunctionReturn(0);
654 }
655 
656 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer) {
657   DM                dm;
658   PetscViewerFormat format;
659   PetscBool         ishdf5;
660 
661   PetscFunctionBegin;
662   PetscCall(VecGetDM(originalv, &dm));
663   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
664   PetscCall(PetscViewerGetFormat(viewer, &format));
665   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
666   if (format == PETSC_VIEWER_NATIVE) {
667     if (dm->useNatural) {
668       if (dm->sfNatural) {
669         if (ishdf5) {
670 #if defined(PETSC_HAVE_HDF5)
671           Vec         v;
672           const char *vecname;
673 
674           PetscCall(DMGetGlobalVector(dm, &v));
675           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
676           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
677           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
678           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
679           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
680           PetscCall(DMRestoreGlobalVector(dm, &v));
681 #else
682           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
683 #endif
684         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
685       }
686     } else PetscCall(VecLoad_Default(originalv, viewer));
687   }
688   PetscFunctionReturn(0);
689 }
690 
691 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer) {
692   PetscSection       coordSection;
693   Vec                coordinates;
694   DMLabel            depthLabel, celltypeLabel;
695   const char        *name[4];
696   const PetscScalar *a;
697   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
698 
699   PetscFunctionBegin;
700   PetscCall(DMGetDimension(dm, &dim));
701   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
702   PetscCall(DMGetCoordinateSection(dm, &coordSection));
703   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
704   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
705   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
706   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
707   PetscCall(VecGetArrayRead(coordinates, &a));
708   name[0]       = "vertex";
709   name[1]       = "edge";
710   name[dim - 1] = "face";
711   name[dim]     = "cell";
712   for (c = cStart; c < cEnd; ++c) {
713     PetscInt *closure = NULL;
714     PetscInt  closureSize, cl, ct;
715 
716     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
717     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
718     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
719     PetscCall(PetscViewerASCIIPushTab(viewer));
720     for (cl = 0; cl < closureSize * 2; cl += 2) {
721       PetscInt point = closure[cl], depth, dof, off, d, p;
722 
723       if ((point < pStart) || (point >= pEnd)) continue;
724       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
725       if (!dof) continue;
726       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
727       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
728       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
729       for (p = 0; p < dof / dim; ++p) {
730         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
731         for (d = 0; d < dim; ++d) {
732           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
733           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
734         }
735         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
736       }
737       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
738     }
739     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
740     PetscCall(PetscViewerASCIIPopTab(viewer));
741   }
742   PetscCall(VecRestoreArrayRead(coordinates, &a));
743   PetscFunctionReturn(0);
744 }
745 
746 typedef enum {
747   CS_CARTESIAN,
748   CS_POLAR,
749   CS_CYLINDRICAL,
750   CS_SPHERICAL
751 } CoordSystem;
752 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
753 
754 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[]) {
755   PetscInt i;
756 
757   PetscFunctionBegin;
758   if (dim > 3) {
759     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
760   } else {
761     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
762 
763     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
764     switch (cs) {
765     case CS_CARTESIAN:
766       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
767       break;
768     case CS_POLAR:
769       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
770       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
771       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
772       break;
773     case CS_CYLINDRICAL:
774       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
775       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
776       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
777       trcoords[2] = coords[2];
778       break;
779     case CS_SPHERICAL:
780       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
781       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
782       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
783       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
784       break;
785     }
786     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
787   }
788   PetscFunctionReturn(0);
789 }
790 
791 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer) {
792   DM_Plex          *mesh = (DM_Plex *)dm->data;
793   DM                cdm, cdmCell;
794   PetscSection      coordSection, coordSectionCell;
795   Vec               coordinates, coordinatesCell;
796   PetscViewerFormat format;
797 
798   PetscFunctionBegin;
799   PetscCall(DMGetCoordinateDM(dm, &cdm));
800   PetscCall(DMGetCoordinateSection(dm, &coordSection));
801   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
802   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
803   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
804   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
805   PetscCall(PetscViewerGetFormat(viewer, &format));
806   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
807     const char *name;
808     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
809     PetscInt    pStart, pEnd, p, numLabels, l;
810     PetscMPIInt rank, size;
811 
812     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
813     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
814     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
815     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
816     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
817     PetscCall(DMGetDimension(dm, &dim));
818     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
819     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
820     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
821     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
822     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
823     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
824     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
825     for (p = pStart; p < pEnd; ++p) {
826       PetscInt dof, off, s;
827 
828       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
829       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
830       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
831     }
832     PetscCall(PetscViewerFlush(viewer));
833     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
834     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
835     for (p = pStart; p < pEnd; ++p) {
836       PetscInt dof, off, c;
837 
838       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
839       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
840       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]));
841     }
842     PetscCall(PetscViewerFlush(viewer));
843     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
844     if (coordSection && coordinates) {
845       CoordSystem        cs = CS_CARTESIAN;
846       const PetscScalar *array, *arrayCell = NULL;
847       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
848       PetscMPIInt        rank;
849       const char        *name;
850 
851       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
852       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
853       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
854       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
855       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
856       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
857       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
858       pStart = PetscMin(pvStart, pcStart);
859       pEnd   = PetscMax(pvEnd, pcEnd);
860       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
861       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
862       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
863       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
864 
865       PetscCall(VecGetArrayRead(coordinates, &array));
866       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
867       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
868       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
869       for (p = pStart; p < pEnd; ++p) {
870         PetscInt dof, off;
871 
872         if (p >= pvStart && p < pvEnd) {
873           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
874           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
875           if (dof) {
876             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
877             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
878             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
879           }
880         }
881         if (cdmCell && p >= pcStart && p < pcEnd) {
882           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
883           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
884           if (dof) {
885             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
886             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
887             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
888           }
889         }
890       }
891       PetscCall(PetscViewerFlush(viewer));
892       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
893       PetscCall(VecRestoreArrayRead(coordinates, &array));
894       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
895     }
896     PetscCall(DMGetNumLabels(dm, &numLabels));
897     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
898     for (l = 0; l < numLabels; ++l) {
899       DMLabel     label;
900       PetscBool   isdepth;
901       const char *name;
902 
903       PetscCall(DMGetLabelName(dm, l, &name));
904       PetscCall(PetscStrcmp(name, "depth", &isdepth));
905       if (isdepth) continue;
906       PetscCall(DMGetLabel(dm, name, &label));
907       PetscCall(DMLabelView(label, viewer));
908     }
909     if (size > 1) {
910       PetscSF sf;
911 
912       PetscCall(DMGetPointSF(dm, &sf));
913       PetscCall(PetscSFView(sf, viewer));
914     }
915     PetscCall(PetscViewerFlush(viewer));
916   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
917     const char  *name, *color;
918     const char  *defcolors[3]  = {"gray", "orange", "green"};
919     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
920     char         lname[PETSC_MAX_PATH_LEN];
921     PetscReal    scale      = 2.0;
922     PetscReal    tikzscale  = 1.0;
923     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
924     double       tcoords[3];
925     PetscScalar *coords;
926     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
927     PetscMPIInt  rank, size;
928     char       **names, **colors, **lcolors;
929     PetscBool    flg, lflg;
930     PetscBT      wp = NULL;
931     PetscInt     pEnd, pStart;
932 
933     PetscCall(DMGetDimension(dm, &dim));
934     PetscCall(DMPlexGetDepth(dm, &depth));
935     PetscCall(DMGetNumLabels(dm, &numLabels));
936     numLabels  = PetscMax(numLabels, 10);
937     numColors  = 10;
938     numLColors = 10;
939     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
940     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
941     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
942     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
943     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
944     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
945     n = 4;
946     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
947     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
948     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
949     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
950     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
951     if (!useLabels) numLabels = 0;
952     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
953     if (!useColors) {
954       numColors = 3;
955       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
956     }
957     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
958     if (!useColors) {
959       numLColors = 4;
960       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
961     }
962     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
963     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
964     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
965     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
966     if (depth < dim) plotEdges = PETSC_FALSE;
967     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
968 
969     /* filter points with labelvalue != labeldefaultvalue */
970     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
971     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
972     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
973     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
974     if (lflg) {
975       DMLabel lbl;
976 
977       PetscCall(DMGetLabel(dm, lname, &lbl));
978       if (lbl) {
979         PetscInt val, defval;
980 
981         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
982         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
983         for (c = pStart; c < pEnd; c++) {
984           PetscInt *closure = NULL;
985           PetscInt  closureSize;
986 
987           PetscCall(DMLabelGetValue(lbl, c, &val));
988           if (val == defval) continue;
989 
990           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
991           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
992           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
993         }
994       }
995     }
996 
997     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
998     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
999     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1000     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1001 \\documentclass[tikz]{standalone}\n\n\
1002 \\usepackage{pgflibraryshapes}\n\
1003 \\usetikzlibrary{backgrounds}\n\
1004 \\usetikzlibrary{arrows}\n\
1005 \\begin{document}\n"));
1006     if (size > 1) {
1007       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1008       for (p = 0; p < size; ++p) {
1009         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1010         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1011       }
1012       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1013     }
1014     if (drawHasse) {
1015       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1016 
1017       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1018       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1019       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1020       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1021       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1022       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1023       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1024       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1025       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1026       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1027       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1028       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1029     }
1030     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1031 
1032     /* Plot vertices */
1033     PetscCall(VecGetArray(coordinates, &coords));
1034     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1035     for (v = vStart; v < vEnd; ++v) {
1036       PetscInt  off, dof, d;
1037       PetscBool isLabeled = PETSC_FALSE;
1038 
1039       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1040       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1041       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1042       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1043       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1044       for (d = 0; d < dof; ++d) {
1045         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1046         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1047       }
1048       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1049       if (dim == 3) {
1050         PetscReal tmp = tcoords[1];
1051         tcoords[1]    = tcoords[2];
1052         tcoords[2]    = -tmp;
1053       }
1054       for (d = 0; d < dof; ++d) {
1055         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1056         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1057       }
1058       if (drawHasse) color = colors[0 % numColors];
1059       else color = colors[rank % numColors];
1060       for (l = 0; l < numLabels; ++l) {
1061         PetscInt val;
1062         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1063         if (val >= 0) {
1064           color     = lcolors[l % numLColors];
1065           isLabeled = PETSC_TRUE;
1066           break;
1067         }
1068       }
1069       if (drawNumbers[0]) {
1070         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1071       } else if (drawColors[0]) {
1072         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1073       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1074     }
1075     PetscCall(VecRestoreArray(coordinates, &coords));
1076     PetscCall(PetscViewerFlush(viewer));
1077     /* Plot edges */
1078     if (plotEdges) {
1079       PetscCall(VecGetArray(coordinates, &coords));
1080       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1081       for (e = eStart; e < eEnd; ++e) {
1082         const PetscInt *cone;
1083         PetscInt        coneSize, offA, offB, dof, d;
1084 
1085         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1086         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1087         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1088         PetscCall(DMPlexGetCone(dm, e, &cone));
1089         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1090         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1091         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1092         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1093         for (d = 0; d < dof; ++d) {
1094           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1095           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1096         }
1097         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1098         if (dim == 3) {
1099           PetscReal tmp = tcoords[1];
1100           tcoords[1]    = tcoords[2];
1101           tcoords[2]    = -tmp;
1102         }
1103         for (d = 0; d < dof; ++d) {
1104           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1105           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1106         }
1107         if (drawHasse) color = colors[1 % numColors];
1108         else color = colors[rank % numColors];
1109         for (l = 0; l < numLabels; ++l) {
1110           PetscInt val;
1111           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1112           if (val >= 0) {
1113             color = lcolors[l % numLColors];
1114             break;
1115           }
1116         }
1117         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1118       }
1119       PetscCall(VecRestoreArray(coordinates, &coords));
1120       PetscCall(PetscViewerFlush(viewer));
1121       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1122     }
1123     /* Plot cells */
1124     if (dim == 3 || !drawNumbers[1]) {
1125       for (e = eStart; e < eEnd; ++e) {
1126         const PetscInt *cone;
1127 
1128         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1129         color = colors[rank % numColors];
1130         for (l = 0; l < numLabels; ++l) {
1131           PetscInt val;
1132           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1133           if (val >= 0) {
1134             color = lcolors[l % numLColors];
1135             break;
1136           }
1137         }
1138         PetscCall(DMPlexGetCone(dm, e, &cone));
1139         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1140       }
1141     } else {
1142       DMPolytopeType ct;
1143 
1144       /* Drawing a 2D polygon */
1145       for (c = cStart; c < cEnd; ++c) {
1146         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1147         PetscCall(DMPlexGetCellType(dm, c, &ct));
1148         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1149           const PetscInt *cone;
1150           PetscInt        coneSize, e;
1151 
1152           PetscCall(DMPlexGetCone(dm, c, &cone));
1153           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1154           for (e = 0; e < coneSize; ++e) {
1155             const PetscInt *econe;
1156 
1157             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1158             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));
1159           }
1160         } else {
1161           PetscInt *closure = NULL;
1162           PetscInt  closureSize, Nv = 0, v;
1163 
1164           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1165           for (p = 0; p < closureSize * 2; p += 2) {
1166             const PetscInt point = closure[p];
1167 
1168             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1169           }
1170           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1171           for (v = 0; v <= Nv; ++v) {
1172             const PetscInt vertex = closure[v % Nv];
1173 
1174             if (v > 0) {
1175               if (plotEdges) {
1176                 const PetscInt *edge;
1177                 PetscInt        endpoints[2], ne;
1178 
1179                 endpoints[0] = closure[v - 1];
1180                 endpoints[1] = vertex;
1181                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1182                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1183                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1184                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1185               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1186             }
1187             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1188           }
1189           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1190           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1191         }
1192       }
1193     }
1194     for (c = cStart; c < cEnd; ++c) {
1195       double             ccoords[3] = {0.0, 0.0, 0.0};
1196       PetscBool          isLabeled  = PETSC_FALSE;
1197       PetscScalar       *cellCoords = NULL;
1198       const PetscScalar *array;
1199       PetscInt           numCoords, cdim, d;
1200       PetscBool          isDG;
1201 
1202       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1203       PetscCall(DMGetCoordinateDim(dm, &cdim));
1204       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1205       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1206       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1207       for (p = 0; p < numCoords / cdim; ++p) {
1208         for (d = 0; d < cdim; ++d) {
1209           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1210           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1211         }
1212         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1213         if (cdim == 3) {
1214           PetscReal tmp = tcoords[1];
1215           tcoords[1]    = tcoords[2];
1216           tcoords[2]    = -tmp;
1217         }
1218         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1219       }
1220       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1221       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1222       for (d = 0; d < cdim; ++d) {
1223         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1224         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1225       }
1226       if (drawHasse) color = colors[depth % numColors];
1227       else color = colors[rank % numColors];
1228       for (l = 0; l < numLabels; ++l) {
1229         PetscInt val;
1230         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1231         if (val >= 0) {
1232           color     = lcolors[l % numLColors];
1233           isLabeled = PETSC_TRUE;
1234           break;
1235         }
1236       }
1237       if (drawNumbers[dim]) {
1238         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1239       } else if (drawColors[dim]) {
1240         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1241       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1242     }
1243     if (drawHasse) {
1244       color = colors[depth % numColors];
1245       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1246       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1247       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1248       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1249       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1250 
1251       color = colors[1 % numColors];
1252       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1253       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1255       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1256       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1257 
1258       color = colors[0 % numColors];
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1262       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1263       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1264 
1265       for (p = pStart; p < pEnd; ++p) {
1266         const PetscInt *cone;
1267         PetscInt        coneSize, cp;
1268 
1269         PetscCall(DMPlexGetCone(dm, p, &cone));
1270         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1271         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1272       }
1273     }
1274     PetscCall(PetscViewerFlush(viewer));
1275     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1276     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1277     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1278     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1279     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1280     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1281     PetscCall(PetscFree3(names, colors, lcolors));
1282     PetscCall(PetscBTDestroy(&wp));
1283   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1284     Vec                    cown, acown;
1285     VecScatter             sct;
1286     ISLocalToGlobalMapping g2l;
1287     IS                     gid, acis;
1288     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1289     MPI_Group              ggroup, ngroup;
1290     PetscScalar           *array, nid;
1291     const PetscInt        *idxs;
1292     PetscInt              *idxs2, *start, *adjacency, *work;
1293     PetscInt64             lm[3], gm[3];
1294     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1295     PetscMPIInt            d1, d2, rank;
1296 
1297     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1298     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1299 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1300     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1301 #endif
1302     if (ncomm != MPI_COMM_NULL) {
1303       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1304       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1305       d1 = 0;
1306       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1307       nid = d2;
1308       PetscCallMPI(MPI_Group_free(&ggroup));
1309       PetscCallMPI(MPI_Group_free(&ngroup));
1310       PetscCallMPI(MPI_Comm_free(&ncomm));
1311     } else nid = 0.0;
1312 
1313     /* Get connectivity */
1314     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1315     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1316 
1317     /* filter overlapped local cells */
1318     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1319     PetscCall(ISGetIndices(gid, &idxs));
1320     PetscCall(ISGetLocalSize(gid, &cum));
1321     PetscCall(PetscMalloc1(cum, &idxs2));
1322     for (c = cStart, cum = 0; c < cEnd; c++) {
1323       if (idxs[c - cStart] < 0) continue;
1324       idxs2[cum++] = idxs[c - cStart];
1325     }
1326     PetscCall(ISRestoreIndices(gid, &idxs));
1327     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1328     PetscCall(ISDestroy(&gid));
1329     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1330 
1331     /* support for node-aware cell locality */
1332     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1333     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1334     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1335     PetscCall(VecGetArray(cown, &array));
1336     for (c = 0; c < numVertices; c++) array[c] = nid;
1337     PetscCall(VecRestoreArray(cown, &array));
1338     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1339     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1340     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1341     PetscCall(ISDestroy(&acis));
1342     PetscCall(VecScatterDestroy(&sct));
1343     PetscCall(VecDestroy(&cown));
1344 
1345     /* compute edgeCut */
1346     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1347     PetscCall(PetscMalloc1(cum, &work));
1348     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1349     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1350     PetscCall(ISDestroy(&gid));
1351     PetscCall(VecGetArray(acown, &array));
1352     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1353       PetscInt totl;
1354 
1355       totl = start[c + 1] - start[c];
1356       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1357       for (i = 0; i < totl; i++) {
1358         if (work[i] < 0) {
1359           ect += 1;
1360           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1361         }
1362       }
1363     }
1364     PetscCall(PetscFree(work));
1365     PetscCall(VecRestoreArray(acown, &array));
1366     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1367     lm[1] = -numVertices;
1368     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1369     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1370     lm[0] = ect;                     /* edgeCut */
1371     lm[1] = ectn;                    /* node-aware edgeCut */
1372     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1373     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1374     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1375 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1376     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1377 #else
1378     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1379 #endif
1380     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1381     PetscCall(PetscFree(start));
1382     PetscCall(PetscFree(adjacency));
1383     PetscCall(VecDestroy(&acown));
1384   } else {
1385     const char    *name;
1386     PetscInt      *sizes, *hybsizes, *ghostsizes;
1387     PetscInt       locDepth, depth, cellHeight, dim, d;
1388     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1389     PetscInt       numLabels, l, maxSize = 17;
1390     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1391     MPI_Comm       comm;
1392     PetscMPIInt    size, rank;
1393 
1394     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1395     PetscCallMPI(MPI_Comm_size(comm, &size));
1396     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1397     PetscCall(DMGetDimension(dm, &dim));
1398     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1399     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1400     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1401     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1402     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1403     PetscCall(DMPlexGetDepth(dm, &locDepth));
1404     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1405     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1406     gcNum = gcEnd - gcStart;
1407     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1408     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1409     for (d = 0; d <= depth; d++) {
1410       PetscInt Nc[2] = {0, 0}, ict;
1411 
1412       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1413       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1414       ict = ct0;
1415       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1416       ct0 = (DMPolytopeType)ict;
1417       for (p = pStart; p < pEnd; ++p) {
1418         DMPolytopeType ct;
1419 
1420         PetscCall(DMPlexGetCellType(dm, p, &ct));
1421         if (ct == ct0) ++Nc[0];
1422         else ++Nc[1];
1423       }
1424       if (size < maxSize) {
1425         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1426         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1427         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1428         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1429         for (p = 0; p < size; ++p) {
1430           if (rank == 0) {
1431             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1432             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1433             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1434           }
1435         }
1436       } else {
1437         PetscInt locMinMax[2];
1438 
1439         locMinMax[0] = Nc[0] + Nc[1];
1440         locMinMax[1] = Nc[0] + Nc[1];
1441         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1442         locMinMax[0] = Nc[1];
1443         locMinMax[1] = Nc[1];
1444         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1445         if (d == depth) {
1446           locMinMax[0] = gcNum;
1447           locMinMax[1] = gcNum;
1448           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1449         }
1450         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1451         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1452         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1453         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1454       }
1455       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1456     }
1457     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1458     {
1459       const PetscReal *maxCell;
1460       const PetscReal *L;
1461       PetscBool        localized;
1462 
1463       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1464       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1465       if (L || localized) {
1466         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1467         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1468         if (L) {
1469           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1470           for (d = 0; d < dim; ++d) {
1471             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1472             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1473           }
1474           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1475         }
1476         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1477         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1478       }
1479     }
1480     PetscCall(DMGetNumLabels(dm, &numLabels));
1481     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1482     for (l = 0; l < numLabels; ++l) {
1483       DMLabel         label;
1484       const char     *name;
1485       IS              valueIS;
1486       const PetscInt *values;
1487       PetscInt        numValues, v;
1488 
1489       PetscCall(DMGetLabelName(dm, l, &name));
1490       PetscCall(DMGetLabel(dm, name, &label));
1491       PetscCall(DMLabelGetNumValues(label, &numValues));
1492       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1493       PetscCall(DMLabelGetValueIS(label, &valueIS));
1494       PetscCall(ISGetIndices(valueIS, &values));
1495       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1496       for (v = 0; v < numValues; ++v) {
1497         PetscInt size;
1498 
1499         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1500         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1501         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1502       }
1503       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1504       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1505       PetscCall(ISRestoreIndices(valueIS, &values));
1506       PetscCall(ISDestroy(&valueIS));
1507     }
1508     {
1509       char    **labelNames;
1510       PetscInt  Nl = numLabels;
1511       PetscBool flg;
1512 
1513       PetscCall(PetscMalloc1(Nl, &labelNames));
1514       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1515       for (l = 0; l < Nl; ++l) {
1516         DMLabel label;
1517 
1518         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1519         if (flg) {
1520           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1521           PetscCall(DMLabelView(label, viewer));
1522         }
1523         PetscCall(PetscFree(labelNames[l]));
1524       }
1525       PetscCall(PetscFree(labelNames));
1526     }
1527     /* If no fields are specified, people do not want to see adjacency */
1528     if (dm->Nf) {
1529       PetscInt f;
1530 
1531       for (f = 0; f < dm->Nf; ++f) {
1532         const char *name;
1533 
1534         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1535         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1536         PetscCall(PetscViewerASCIIPushTab(viewer));
1537         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1538         if (dm->fields[f].adjacency[0]) {
1539           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1540           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1541         } else {
1542           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1543           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1544         }
1545         PetscCall(PetscViewerASCIIPopTab(viewer));
1546       }
1547     }
1548     PetscCall(DMGetCoarseDM(dm, &cdm));
1549     if (cdm) {
1550       PetscCall(PetscViewerASCIIPushTab(viewer));
1551       PetscCall(DMPlexView_Ascii(cdm, viewer));
1552       PetscCall(PetscViewerASCIIPopTab(viewer));
1553     }
1554   }
1555   PetscFunctionReturn(0);
1556 }
1557 
1558 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[]) {
1559   DMPolytopeType ct;
1560   PetscMPIInt    rank;
1561   PetscInt       cdim;
1562 
1563   PetscFunctionBegin;
1564   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1565   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1566   PetscCall(DMGetCoordinateDim(dm, &cdim));
1567   switch (ct) {
1568   case DM_POLYTOPE_SEGMENT:
1569   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1570     switch (cdim) {
1571     case 1: {
1572       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1573       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1574 
1575       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1576       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1577       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1578     } break;
1579     case 2: {
1580       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1581       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1582       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1583 
1584       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1585       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));
1586       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));
1587     } break;
1588     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1589     }
1590     break;
1591   case DM_POLYTOPE_TRIANGLE:
1592     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));
1593     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1594     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1595     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1596     break;
1597   case DM_POLYTOPE_QUADRILATERAL:
1598     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));
1599     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));
1600     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1601     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1602     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1603     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1604     break;
1605   case DM_POLYTOPE_FV_GHOST: break;
1606   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1607   }
1608   PetscFunctionReturn(0);
1609 }
1610 
1611 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[]) {
1612   DMPolytopeType ct;
1613   PetscReal      centroid[2] = {0., 0.};
1614   PetscMPIInt    rank;
1615   PetscInt       fillColor, v, e, d;
1616 
1617   PetscFunctionBegin;
1618   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1619   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1620   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1621   switch (ct) {
1622   case DM_POLYTOPE_TRIANGLE: {
1623     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1624 
1625     for (v = 0; v < 3; ++v) {
1626       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1627       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1628     }
1629     for (e = 0; e < 3; ++e) {
1630       refCoords[0] = refVertices[e * 2 + 0];
1631       refCoords[1] = refVertices[e * 2 + 1];
1632       for (d = 1; d <= edgeDiv; ++d) {
1633         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1634         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1635       }
1636       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1637       for (d = 0; d < edgeDiv; ++d) {
1638         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));
1639         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1640       }
1641     }
1642   } break;
1643   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1644   }
1645   PetscFunctionReturn(0);
1646 }
1647 
1648 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer) {
1649   PetscDraw          draw;
1650   DM                 cdm;
1651   PetscSection       coordSection;
1652   Vec                coordinates;
1653   const PetscScalar *coords;
1654   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1655   PetscReal         *refCoords, *edgeCoords;
1656   PetscBool          isnull, drawAffine = PETSC_TRUE;
1657   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1658 
1659   PetscFunctionBegin;
1660   PetscCall(DMGetCoordinateDim(dm, &dim));
1661   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1662   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1663   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1664   PetscCall(DMGetCoordinateDM(dm, &cdm));
1665   PetscCall(DMGetLocalSection(cdm, &coordSection));
1666   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1667   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1668   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1669 
1670   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1671   PetscCall(PetscDrawIsNull(draw, &isnull));
1672   if (isnull) PetscFunctionReturn(0);
1673   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1674 
1675   PetscCall(VecGetLocalSize(coordinates, &N));
1676   PetscCall(VecGetArrayRead(coordinates, &coords));
1677   for (c = 0; c < N; c += dim) {
1678     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1679     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1680     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1681     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1682   }
1683   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1684   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1685   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1686   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1687   PetscCall(PetscDrawClear(draw));
1688 
1689   for (c = cStart; c < cEnd; ++c) {
1690     PetscScalar *coords = NULL;
1691     PetscInt     numCoords;
1692 
1693     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1694     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1695     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1696     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1697   }
1698   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1699   PetscCall(PetscDrawFlush(draw));
1700   PetscCall(PetscDrawPause(draw));
1701   PetscCall(PetscDrawSave(draw));
1702   PetscFunctionReturn(0);
1703 }
1704 
1705 #if defined(PETSC_HAVE_EXODUSII)
1706 #include <exodusII.h>
1707 #include <petscviewerexodusii.h>
1708 #endif
1709 
1710 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer) {
1711   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1712   char      name[PETSC_MAX_PATH_LEN];
1713 
1714   PetscFunctionBegin;
1715   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1716   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1717   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1718   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1719   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1720   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1721   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1722   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1723   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1724   if (iascii) {
1725     PetscViewerFormat format;
1726     PetscCall(PetscViewerGetFormat(viewer, &format));
1727     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1728     else PetscCall(DMPlexView_Ascii(dm, viewer));
1729   } else if (ishdf5) {
1730 #if defined(PETSC_HAVE_HDF5)
1731     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1732 #else
1733     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1734 #endif
1735   } else if (isvtk) {
1736     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1737   } else if (isdraw) {
1738     PetscCall(DMPlexView_Draw(dm, viewer));
1739   } else if (isglvis) {
1740     PetscCall(DMPlexView_GLVis(dm, viewer));
1741 #if defined(PETSC_HAVE_EXODUSII)
1742   } else if (isexodus) {
1743     /*
1744       exodusII requires that all sets be part of exactly one cell set.
1745       If the dm does not have a "Cell Sets" label defined, we create one
1746       with ID 1, containig all cells.
1747       Note that if the Cell Sets label is defined but does not cover all cells,
1748       we may still have a problem. This should probably be checked here or in the viewer;
1749     */
1750     PetscInt numCS;
1751     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1752     if (!numCS) {
1753       PetscInt cStart, cEnd, c;
1754       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1755       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1756       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1757     }
1758     PetscCall(DMView_PlexExodusII(dm, viewer));
1759 #endif
1760 #if defined(PETSC_HAVE_CGNS)
1761   } else if (iscgns) {
1762     PetscCall(DMView_PlexCGNS(dm, viewer));
1763 #endif
1764   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1765   /* Optionally view the partition */
1766   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1767   if (flg) {
1768     Vec ranks;
1769     PetscCall(DMPlexCreateRankField(dm, &ranks));
1770     PetscCall(VecView(ranks, viewer));
1771     PetscCall(VecDestroy(&ranks));
1772   }
1773   /* Optionally view a label */
1774   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1775   if (flg) {
1776     DMLabel label;
1777     Vec     val;
1778 
1779     PetscCall(DMGetLabel(dm, name, &label));
1780     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1781     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1782     PetscCall(VecView(val, viewer));
1783     PetscCall(VecDestroy(&val));
1784   }
1785   PetscFunctionReturn(0);
1786 }
1787 
1788 /*@
1789   DMPlexTopologyView - Saves a DMPlex topology into a file
1790 
1791   Collective on DM
1792 
1793   Input Parameters:
1794 + dm                - The DM whose topology is to be saved
1795 - viewer            - The PetscViewer for saving
1796 
1797   Level: advanced
1798 
1799 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1800 @*/
1801 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer) {
1802   PetscBool ishdf5;
1803 
1804   PetscFunctionBegin;
1805   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1806   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1807   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1808   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1809   if (ishdf5) {
1810 #if defined(PETSC_HAVE_HDF5)
1811     PetscViewerFormat format;
1812     PetscCall(PetscViewerGetFormat(viewer, &format));
1813     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1814       IS globalPointNumbering;
1815 
1816       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1817       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1818       PetscCall(ISDestroy(&globalPointNumbering));
1819     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1820 #else
1821     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1822 #endif
1823   }
1824   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1825   PetscFunctionReturn(0);
1826 }
1827 
1828 /*@
1829   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1830 
1831   Collective on DM
1832 
1833   Input Parameters:
1834 + dm     - The DM whose coordinates are to be saved
1835 - viewer - The PetscViewer for saving
1836 
1837   Level: advanced
1838 
1839 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1840 @*/
1841 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer) {
1842   PetscBool ishdf5;
1843 
1844   PetscFunctionBegin;
1845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1846   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1847   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1848   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1849   if (ishdf5) {
1850 #if defined(PETSC_HAVE_HDF5)
1851     PetscViewerFormat format;
1852     PetscCall(PetscViewerGetFormat(viewer, &format));
1853     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1854       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1855     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1856 #else
1857     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1858 #endif
1859   }
1860   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1861   PetscFunctionReturn(0);
1862 }
1863 
1864 /*@
1865   DMPlexLabelsView - Saves DMPlex labels into a file
1866 
1867   Collective on DM
1868 
1869   Input Parameters:
1870 + dm     - The DM whose labels are to be saved
1871 - viewer - The PetscViewer for saving
1872 
1873   Level: advanced
1874 
1875 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1876 @*/
1877 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer) {
1878   PetscBool ishdf5;
1879 
1880   PetscFunctionBegin;
1881   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1882   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1883   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1884   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1885   if (ishdf5) {
1886 #if defined(PETSC_HAVE_HDF5)
1887     IS                globalPointNumbering;
1888     PetscViewerFormat format;
1889 
1890     PetscCall(PetscViewerGetFormat(viewer, &format));
1891     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1892       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1893       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1894       PetscCall(ISDestroy(&globalPointNumbering));
1895     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1896 #else
1897     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1898 #endif
1899   }
1900   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1901   PetscFunctionReturn(0);
1902 }
1903 
1904 /*@
1905   DMPlexSectionView - Saves a section associated with a DMPlex
1906 
1907   Collective on DM
1908 
1909   Input Parameters:
1910 + dm         - The DM that contains the topology on which the section to be saved is defined
1911 . viewer     - The PetscViewer for saving
1912 - sectiondm  - The DM that contains the section to be saved
1913 
1914   Level: advanced
1915 
1916   Notes:
1917   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.
1918 
1919   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.
1920 
1921 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1922 @*/
1923 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm) {
1924   PetscBool ishdf5;
1925 
1926   PetscFunctionBegin;
1927   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1928   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1929   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1930   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1931   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1932   if (ishdf5) {
1933 #if defined(PETSC_HAVE_HDF5)
1934     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1935 #else
1936     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1937 #endif
1938   }
1939   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1940   PetscFunctionReturn(0);
1941 }
1942 
1943 /*@
1944   DMPlexGlobalVectorView - Saves a global vector
1945 
1946   Collective on DM
1947 
1948   Input Parameters:
1949 + dm        - The DM that represents the topology
1950 . viewer    - The PetscViewer to save data with
1951 . sectiondm - The DM that contains the global section on which vec is defined
1952 - vec       - The global vector to be saved
1953 
1954   Level: advanced
1955 
1956   Notes:
1957   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.
1958 
1959   Typical calling sequence
1960 $       DMCreate(PETSC_COMM_WORLD, &dm);
1961 $       DMSetType(dm, DMPLEX);
1962 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1963 $       DMClone(dm, &sectiondm);
1964 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1965 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1966 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1967 $       PetscSectionSetChart(section, pStart, pEnd);
1968 $       PetscSectionSetUp(section);
1969 $       DMSetLocalSection(sectiondm, section);
1970 $       PetscSectionDestroy(&section);
1971 $       DMGetGlobalVector(sectiondm, &vec);
1972 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1973 $       DMPlexTopologyView(dm, viewer);
1974 $       DMPlexSectionView(dm, viewer, sectiondm);
1975 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1976 $       DMRestoreGlobalVector(sectiondm, &vec);
1977 $       DMDestroy(&sectiondm);
1978 $       DMDestroy(&dm);
1979 
1980 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1981 @*/
1982 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec) {
1983   PetscBool ishdf5;
1984 
1985   PetscFunctionBegin;
1986   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1987   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1988   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1989   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1990   /* Check consistency */
1991   {
1992     PetscSection section;
1993     PetscBool    includesConstraints;
1994     PetscInt     m, m1;
1995 
1996     PetscCall(VecGetLocalSize(vec, &m1));
1997     PetscCall(DMGetGlobalSection(sectiondm, &section));
1998     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1999     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2000     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2001     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2002   }
2003   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2004   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2005   if (ishdf5) {
2006 #if defined(PETSC_HAVE_HDF5)
2007     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2008 #else
2009     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2010 #endif
2011   }
2012   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2013   PetscFunctionReturn(0);
2014 }
2015 
2016 /*@
2017   DMPlexLocalVectorView - Saves a local vector
2018 
2019   Collective on DM
2020 
2021   Input Parameters:
2022 + dm        - The DM that represents the topology
2023 . viewer    - The PetscViewer to save data with
2024 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2025 - vec       - The local vector to be saved
2026 
2027   Level: advanced
2028 
2029   Notes:
2030   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.
2031 
2032   Typical calling sequence
2033 $       DMCreate(PETSC_COMM_WORLD, &dm);
2034 $       DMSetType(dm, DMPLEX);
2035 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2036 $       DMClone(dm, &sectiondm);
2037 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2038 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2039 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2040 $       PetscSectionSetChart(section, pStart, pEnd);
2041 $       PetscSectionSetUp(section);
2042 $       DMSetLocalSection(sectiondm, section);
2043 $       DMGetLocalVector(sectiondm, &vec);
2044 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2045 $       DMPlexTopologyView(dm, viewer);
2046 $       DMPlexSectionView(dm, viewer, sectiondm);
2047 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2048 $       DMRestoreLocalVector(sectiondm, &vec);
2049 $       DMDestroy(&sectiondm);
2050 $       DMDestroy(&dm);
2051 
2052 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2053 @*/
2054 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec) {
2055   PetscBool ishdf5;
2056 
2057   PetscFunctionBegin;
2058   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2059   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2060   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2061   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2062   /* Check consistency */
2063   {
2064     PetscSection section;
2065     PetscBool    includesConstraints;
2066     PetscInt     m, m1;
2067 
2068     PetscCall(VecGetLocalSize(vec, &m1));
2069     PetscCall(DMGetLocalSection(sectiondm, &section));
2070     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2071     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2072     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2073     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2074   }
2075   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2076   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2077   if (ishdf5) {
2078 #if defined(PETSC_HAVE_HDF5)
2079     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2080 #else
2081     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2082 #endif
2083   }
2084   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2085   PetscFunctionReturn(0);
2086 }
2087 
2088 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer) {
2089   PetscBool ishdf5;
2090 
2091   PetscFunctionBegin;
2092   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2093   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2094   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2095   if (ishdf5) {
2096 #if defined(PETSC_HAVE_HDF5)
2097     PetscViewerFormat format;
2098     PetscCall(PetscViewerGetFormat(viewer, &format));
2099     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2100       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2101     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2102       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2103     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2104     PetscFunctionReturn(0);
2105 #else
2106     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2107 #endif
2108   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2109 }
2110 
2111 /*@
2112   DMPlexTopologyLoad - Loads a topology into a DMPlex
2113 
2114   Collective on DM
2115 
2116   Input Parameters:
2117 + dm                - The DM into which the topology is loaded
2118 - viewer            - The PetscViewer for the saved topology
2119 
2120   Output Parameters:
2121 . 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
2122 
2123   Level: advanced
2124 
2125 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2126 @*/
2127 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF) {
2128   PetscBool ishdf5;
2129 
2130   PetscFunctionBegin;
2131   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2132   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2133   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2134   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2135   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2136   if (ishdf5) {
2137 #if defined(PETSC_HAVE_HDF5)
2138     PetscViewerFormat format;
2139     PetscCall(PetscViewerGetFormat(viewer, &format));
2140     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2141       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2142     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2143 #else
2144     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2145 #endif
2146   }
2147   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2148   PetscFunctionReturn(0);
2149 }
2150 
2151 /*@
2152   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2153 
2154   Collective on DM
2155 
2156   Input Parameters:
2157 + dm     - The DM into which the coordinates are loaded
2158 . viewer - The PetscViewer for the saved coordinates
2159 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2160 
2161   Level: advanced
2162 
2163 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2164 @*/
2165 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF) {
2166   PetscBool ishdf5;
2167 
2168   PetscFunctionBegin;
2169   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2170   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2171   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2172   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2173   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2174   if (ishdf5) {
2175 #if defined(PETSC_HAVE_HDF5)
2176     PetscViewerFormat format;
2177     PetscCall(PetscViewerGetFormat(viewer, &format));
2178     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2179       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2180     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2181 #else
2182     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2183 #endif
2184   }
2185   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2186   PetscFunctionReturn(0);
2187 }
2188 
2189 /*@
2190   DMPlexLabelsLoad - Loads labels into a DMPlex
2191 
2192   Collective on DM
2193 
2194   Input Parameters:
2195 + dm     - The DM into which the labels are loaded
2196 . viewer - The PetscViewer for the saved labels
2197 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2198 
2199   Level: advanced
2200 
2201   Notes:
2202   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2203 
2204 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2205 @*/
2206 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF) {
2207   PetscBool ishdf5;
2208 
2209   PetscFunctionBegin;
2210   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2211   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2212   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2213   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2214   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2215   if (ishdf5) {
2216 #if defined(PETSC_HAVE_HDF5)
2217     PetscViewerFormat format;
2218 
2219     PetscCall(PetscViewerGetFormat(viewer, &format));
2220     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2221       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2222     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2223 #else
2224     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2225 #endif
2226   }
2227   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2228   PetscFunctionReturn(0);
2229 }
2230 
2231 /*@
2232   DMPlexSectionLoad - Loads section into a DMPlex
2233 
2234   Collective on DM
2235 
2236   Input Parameters:
2237 + dm          - The DM that represents the topology
2238 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2239 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2240 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2241 
2242   Output Parameters
2243 + 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)
2244 - 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)
2245 
2246   Level: advanced
2247 
2248   Notes:
2249   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.
2250 
2251   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.
2252 
2253   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.
2254 
2255   Example using 2 processes:
2256 $  NX (number of points on dm): 4
2257 $  sectionA                   : the on-disk section
2258 $  vecA                       : a vector associated with sectionA
2259 $  sectionB                   : sectiondm's local section constructed in this function
2260 $  vecB (local)               : a vector associated with sectiondm's local section
2261 $  vecB (global)              : a vector associated with sectiondm's global section
2262 $
2263 $                                     rank 0    rank 1
2264 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2265 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2266 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2267 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2268 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2269 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2270 $  sectionB->atlasDof             :     1 0 1 | 1 3
2271 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2272 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2273 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2274 $
2275 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2276 
2277 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2278 @*/
2279 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF) {
2280   PetscBool ishdf5;
2281 
2282   PetscFunctionBegin;
2283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2284   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2285   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2286   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2287   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2288   if (localDofSF) PetscValidPointer(localDofSF, 6);
2289   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2290   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2291   if (ishdf5) {
2292 #if defined(PETSC_HAVE_HDF5)
2293     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2294 #else
2295     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2296 #endif
2297   }
2298   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2299   PetscFunctionReturn(0);
2300 }
2301 
2302 /*@
2303   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2304 
2305   Collective on DM
2306 
2307   Input Parameters:
2308 + dm        - The DM that represents the topology
2309 . viewer    - The PetscViewer that represents the on-disk vector data
2310 . sectiondm - The DM that contains the global section on which vec is defined
2311 . sf        - The SF that migrates the on-disk vector data into vec
2312 - vec       - The global vector to set values of
2313 
2314   Level: advanced
2315 
2316   Notes:
2317   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.
2318 
2319   Typical calling sequence
2320 $       DMCreate(PETSC_COMM_WORLD, &dm);
2321 $       DMSetType(dm, DMPLEX);
2322 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2323 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2324 $       DMClone(dm, &sectiondm);
2325 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2326 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2327 $       DMGetGlobalVector(sectiondm, &vec);
2328 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2329 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2330 $       DMRestoreGlobalVector(sectiondm, &vec);
2331 $       PetscSFDestroy(&gsf);
2332 $       PetscSFDestroy(&sfX);
2333 $       DMDestroy(&sectiondm);
2334 $       DMDestroy(&dm);
2335 
2336 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2337 @*/
2338 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec) {
2339   PetscBool ishdf5;
2340 
2341   PetscFunctionBegin;
2342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2343   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2344   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2345   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2346   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2347   /* Check consistency */
2348   {
2349     PetscSection section;
2350     PetscBool    includesConstraints;
2351     PetscInt     m, m1;
2352 
2353     PetscCall(VecGetLocalSize(vec, &m1));
2354     PetscCall(DMGetGlobalSection(sectiondm, &section));
2355     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2356     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2357     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2358     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2359   }
2360   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2361   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2362   if (ishdf5) {
2363 #if defined(PETSC_HAVE_HDF5)
2364     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
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_GlobalVectorLoad, viewer, 0, 0, 0));
2370   PetscFunctionReturn(0);
2371 }
2372 
2373 /*@
2374   DMPlexLocalVectorLoad - Loads on-disk vector data into a local 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 local section on which vec is defined
2382 . sf        - The SF that migrates the on-disk vector data into vec
2383 - vec       - The local 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 $       DMCreate(PETSC_COMM_WORLD, &dm);
2392 $       DMSetType(dm, DMPLEX);
2393 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2394 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2395 $       DMClone(dm, &sectiondm);
2396 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2397 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2398 $       DMGetLocalVector(sectiondm, &vec);
2399 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2400 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2401 $       DMRestoreLocalVector(sectiondm, &vec);
2402 $       PetscSFDestroy(&lsf);
2403 $       PetscSFDestroy(&sfX);
2404 $       DMDestroy(&sectiondm);
2405 $       DMDestroy(&dm);
2406 
2407 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2408 @*/
2409 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec) {
2410   PetscBool ishdf5;
2411 
2412   PetscFunctionBegin;
2413   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2414   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2415   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2416   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2417   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2418   /* Check consistency */
2419   {
2420     PetscSection section;
2421     PetscBool    includesConstraints;
2422     PetscInt     m, m1;
2423 
2424     PetscCall(VecGetLocalSize(vec, &m1));
2425     PetscCall(DMGetLocalSection(sectiondm, &section));
2426     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2427     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2428     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2429     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2430   }
2431   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2432   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2433   if (ishdf5) {
2434 #if defined(PETSC_HAVE_HDF5)
2435     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2436 #else
2437     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2438 #endif
2439   }
2440   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2441   PetscFunctionReturn(0);
2442 }
2443 
2444 PetscErrorCode DMDestroy_Plex(DM dm) {
2445   DM_Plex *mesh = (DM_Plex *)dm->data;
2446 
2447   PetscFunctionBegin;
2448   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2454   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2455   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2456   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2457   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2458   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2459   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2460   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2461   if (--mesh->refct > 0) PetscFunctionReturn(0);
2462   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2463   PetscCall(PetscFree(mesh->cones));
2464   PetscCall(PetscFree(mesh->coneOrientations));
2465   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2466   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2467   PetscCall(PetscFree(mesh->supports));
2468   PetscCall(PetscFree(mesh->facesTmp));
2469   PetscCall(PetscFree(mesh->tetgenOpts));
2470   PetscCall(PetscFree(mesh->triangleOpts));
2471   PetscCall(PetscFree(mesh->transformType));
2472   PetscCall(PetscFree(mesh->distributionName));
2473   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2474   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2475   PetscCall(ISDestroy(&mesh->subpointIS));
2476   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2477   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2478   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2479   PetscCall(ISDestroy(&mesh->anchorIS));
2480   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2481   PetscCall(PetscFree(mesh->parents));
2482   PetscCall(PetscFree(mesh->childIDs));
2483   PetscCall(PetscSectionDestroy(&mesh->childSection));
2484   PetscCall(PetscFree(mesh->children));
2485   PetscCall(DMDestroy(&mesh->referenceTree));
2486   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2487   PetscCall(PetscFree(mesh->neighbors));
2488   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2489   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2490   PetscCall(PetscFree(mesh));
2491   PetscFunctionReturn(0);
2492 }
2493 
2494 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J) {
2495   PetscSection           sectionGlobal;
2496   PetscInt               bs = -1, mbs;
2497   PetscInt               localSize, localStart = 0;
2498   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2499   MatType                mtype;
2500   ISLocalToGlobalMapping ltog;
2501 
2502   PetscFunctionBegin;
2503   PetscCall(MatInitializePackage());
2504   mtype = dm->mattype;
2505   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2506   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2507   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2508   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2509   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2510   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2511   PetscCall(MatSetType(*J, mtype));
2512   PetscCall(MatSetFromOptions(*J));
2513   PetscCall(MatGetBlockSize(*J, &mbs));
2514   if (mbs > 1) bs = mbs;
2515   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2516   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2517   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2518   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2519   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2520   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2521   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2522   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2523   if (!isShell) {
2524     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2525     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2526     PetscInt  pStart, pEnd, p, dof, cdof;
2527 
2528     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2529 
2530     PetscCall(PetscCalloc1(localSize, &pblocks));
2531     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2532     for (p = pStart; p < pEnd; ++p) {
2533       PetscInt bdof, offset;
2534 
2535       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2536       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2537       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2538       for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2539       dof  = dof < 0 ? -(dof + 1) : dof;
2540       bdof = cdof && (dof - cdof) ? 1 : dof;
2541       if (dof) {
2542         if (bs < 0) {
2543           bs = bdof;
2544         } else if (bs != bdof) {
2545           bs = 1;
2546         }
2547       }
2548     }
2549     /* Must have same blocksize on all procs (some might have no points) */
2550     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2551     bsLocal[1] = bs;
2552     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2553     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2554     else bs = bsMinMax[0];
2555     bs = PetscMax(1, bs);
2556     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2557     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2558       PetscCall(MatSetBlockSize(*J, bs));
2559       PetscCall(MatSetUp(*J));
2560     } else {
2561       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2562       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2563       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2564     }
2565     { // Consolidate blocks
2566       PetscInt nblocks = 0;
2567       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2568         if (pblocks[i] == 0) continue;
2569         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2570         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]);
2571       }
2572       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2573     }
2574     PetscCall(PetscFree(pblocks));
2575   }
2576   PetscCall(MatSetDM(*J, dm));
2577   PetscFunctionReturn(0);
2578 }
2579 
2580 /*@
2581   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2582 
2583   Not collective
2584 
2585   Input Parameter:
2586 . mesh - The DMPlex
2587 
2588   Output Parameters:
2589 . subsection - The subdomain section
2590 
2591   Level: developer
2592 
2593 .seealso:
2594 @*/
2595 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection) {
2596   DM_Plex *mesh = (DM_Plex *)dm->data;
2597 
2598   PetscFunctionBegin;
2599   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2600   if (!mesh->subdomainSection) {
2601     PetscSection section;
2602     PetscSF      sf;
2603 
2604     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2605     PetscCall(DMGetLocalSection(dm, &section));
2606     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2607     PetscCall(PetscSFDestroy(&sf));
2608   }
2609   *subsection = mesh->subdomainSection;
2610   PetscFunctionReturn(0);
2611 }
2612 
2613 /*@
2614   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2615 
2616   Not collective
2617 
2618   Input Parameter:
2619 . mesh - The DMPlex
2620 
2621   Output Parameters:
2622 + pStart - The first mesh point
2623 - pEnd   - The upper bound for mesh points
2624 
2625   Level: beginner
2626 
2627 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2628 @*/
2629 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd) {
2630   DM_Plex *mesh = (DM_Plex *)dm->data;
2631 
2632   PetscFunctionBegin;
2633   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2634   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2635   PetscFunctionReturn(0);
2636 }
2637 
2638 /*@
2639   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2640 
2641   Not collective
2642 
2643   Input Parameters:
2644 + mesh - The DMPlex
2645 . pStart - The first mesh point
2646 - pEnd   - The upper bound for mesh points
2647 
2648   Output Parameters:
2649 
2650   Level: beginner
2651 
2652 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2653 @*/
2654 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd) {
2655   DM_Plex *mesh = (DM_Plex *)dm->data;
2656 
2657   PetscFunctionBegin;
2658   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2659   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2660   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2661   PetscFunctionReturn(0);
2662 }
2663 
2664 /*@
2665   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2666 
2667   Not collective
2668 
2669   Input Parameters:
2670 + mesh - The DMPlex
2671 - p - The point, which must lie in the chart set with DMPlexSetChart()
2672 
2673   Output Parameter:
2674 . size - The cone size for point p
2675 
2676   Level: beginner
2677 
2678 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2679 @*/
2680 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size) {
2681   DM_Plex *mesh = (DM_Plex *)dm->data;
2682 
2683   PetscFunctionBegin;
2684   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2685   PetscValidIntPointer(size, 3);
2686   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2687   PetscFunctionReturn(0);
2688 }
2689 
2690 /*@
2691   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2692 
2693   Not collective
2694 
2695   Input Parameters:
2696 + mesh - The DMPlex
2697 . p - The point, which must lie in the chart set with DMPlexSetChart()
2698 - size - The cone size for point p
2699 
2700   Output Parameter:
2701 
2702   Note:
2703   This should be called after DMPlexSetChart().
2704 
2705   Level: beginner
2706 
2707 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2708 @*/
2709 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size) {
2710   DM_Plex *mesh = (DM_Plex *)dm->data;
2711 
2712   PetscFunctionBegin;
2713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2714   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2715   PetscFunctionReturn(0);
2716 }
2717 
2718 /*@
2719   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2720 
2721   Not collective
2722 
2723   Input Parameters:
2724 + mesh - The DMPlex
2725 . p - The point, which must lie in the chart set with DMPlexSetChart()
2726 - size - The additional cone size for point p
2727 
2728   Output Parameter:
2729 
2730   Note:
2731   This should be called after DMPlexSetChart().
2732 
2733   Level: beginner
2734 
2735 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2736 @*/
2737 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size) {
2738   DM_Plex *mesh = (DM_Plex *)dm->data;
2739   PetscFunctionBegin;
2740   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2741   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2742   PetscFunctionReturn(0);
2743 }
2744 
2745 /*@C
2746   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2747 
2748   Not collective
2749 
2750   Input Parameters:
2751 + dm - The DMPlex
2752 - p - The point, which must lie in the chart set with DMPlexSetChart()
2753 
2754   Output Parameter:
2755 . cone - An array of points which are on the in-edges for point p
2756 
2757   Level: beginner
2758 
2759   Fortran Notes:
2760   Since it returns an array, this routine is only available in Fortran 90, and you must
2761   include petsc.h90 in your code.
2762   You must also call DMPlexRestoreCone() after you finish using the returned array.
2763   DMPlexRestoreCone() is not needed/available in C.
2764 
2765 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2766 @*/
2767 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[]) {
2768   DM_Plex *mesh = (DM_Plex *)dm->data;
2769   PetscInt off;
2770 
2771   PetscFunctionBegin;
2772   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2773   PetscValidPointer(cone, 3);
2774   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2775   *cone = &mesh->cones[off];
2776   PetscFunctionReturn(0);
2777 }
2778 
2779 /*@C
2780   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2781 
2782   Not collective
2783 
2784   Input Parameters:
2785 + dm - The DMPlex
2786 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2787 
2788   Output Parameters:
2789 + pConesSection - PetscSection describing the layout of pCones
2790 - pCones - An array of points which are on the in-edges for the point set p
2791 
2792   Level: intermediate
2793 
2794 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2795 @*/
2796 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones) {
2797   PetscSection cs, newcs;
2798   PetscInt    *cones;
2799   PetscInt    *newarr = NULL;
2800   PetscInt     n;
2801 
2802   PetscFunctionBegin;
2803   PetscCall(DMPlexGetCones(dm, &cones));
2804   PetscCall(DMPlexGetConeSection(dm, &cs));
2805   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2806   if (pConesSection) *pConesSection = newcs;
2807   if (pCones) {
2808     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2809     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2810   }
2811   PetscFunctionReturn(0);
2812 }
2813 
2814 /*@
2815   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2816 
2817   Not collective
2818 
2819   Input Parameters:
2820 + dm - The DMPlex
2821 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2822 
2823   Output Parameter:
2824 . expandedPoints - An array of vertices recursively expanded from input points
2825 
2826   Level: advanced
2827 
2828   Notes:
2829   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2830   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2831 
2832 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2833 @*/
2834 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints) {
2835   IS      *expandedPointsAll;
2836   PetscInt depth;
2837 
2838   PetscFunctionBegin;
2839   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2840   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2841   PetscValidPointer(expandedPoints, 3);
2842   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2843   *expandedPoints = expandedPointsAll[0];
2844   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2845   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2846   PetscFunctionReturn(0);
2847 }
2848 
2849 /*@
2850   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).
2851 
2852   Not collective
2853 
2854   Input Parameters:
2855 + dm - The DMPlex
2856 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2857 
2858   Output Parameters:
2859 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2860 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2861 - sections - (optional) An array of sections which describe mappings from points to their cone points
2862 
2863   Level: advanced
2864 
2865   Notes:
2866   Like DMPlexGetConeTuple() but recursive.
2867 
2868   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.
2869   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2870 
2871   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:
2872   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2873   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2874 
2875 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2876 @*/
2877 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[]) {
2878   const PetscInt *arr0 = NULL, *cone = NULL;
2879   PetscInt       *arr = NULL, *newarr = NULL;
2880   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2881   IS             *expandedPoints_;
2882   PetscSection   *sections_;
2883 
2884   PetscFunctionBegin;
2885   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2886   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2887   if (depth) PetscValidIntPointer(depth, 3);
2888   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2889   if (sections) PetscValidPointer(sections, 5);
2890   PetscCall(ISGetLocalSize(points, &n));
2891   PetscCall(ISGetIndices(points, &arr0));
2892   PetscCall(DMPlexGetDepth(dm, &depth_));
2893   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2894   PetscCall(PetscCalloc1(depth_, &sections_));
2895   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2896   for (d = depth_ - 1; d >= 0; d--) {
2897     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2898     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2899     for (i = 0; i < n; i++) {
2900       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2901       if (arr[i] >= start && arr[i] < end) {
2902         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2903         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2904       } else {
2905         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2906       }
2907     }
2908     PetscCall(PetscSectionSetUp(sections_[d]));
2909     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2910     PetscCall(PetscMalloc1(newn, &newarr));
2911     for (i = 0; i < n; i++) {
2912       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2913       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2914       if (cn > 1) {
2915         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2916         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2917       } else {
2918         newarr[co] = arr[i];
2919       }
2920     }
2921     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2922     arr = newarr;
2923     n   = newn;
2924   }
2925   PetscCall(ISRestoreIndices(points, &arr0));
2926   *depth = depth_;
2927   if (expandedPoints) *expandedPoints = expandedPoints_;
2928   else {
2929     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2930     PetscCall(PetscFree(expandedPoints_));
2931   }
2932   if (sections) *sections = sections_;
2933   else {
2934     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2935     PetscCall(PetscFree(sections_));
2936   }
2937   PetscFunctionReturn(0);
2938 }
2939 
2940 /*@
2941   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2942 
2943   Not collective
2944 
2945   Input Parameters:
2946 + dm - The DMPlex
2947 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2948 
2949   Output Parameters:
2950 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2951 . expandedPoints - (optional) An array of recursively expanded cones
2952 - sections - (optional) An array of sections which describe mappings from points to their cone points
2953 
2954   Level: advanced
2955 
2956   Notes:
2957   See DMPlexGetConeRecursive() for details.
2958 
2959 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2960 @*/
2961 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[]) {
2962   PetscInt d, depth_;
2963 
2964   PetscFunctionBegin;
2965   PetscCall(DMPlexGetDepth(dm, &depth_));
2966   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2967   if (depth) *depth = 0;
2968   if (expandedPoints) {
2969     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2970     PetscCall(PetscFree(*expandedPoints));
2971   }
2972   if (sections) {
2973     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2974     PetscCall(PetscFree(*sections));
2975   }
2976   PetscFunctionReturn(0);
2977 }
2978 
2979 /*@
2980   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
2981 
2982   Not collective
2983 
2984   Input Parameters:
2985 + mesh - The DMPlex
2986 . p - The point, which must lie in the chart set with DMPlexSetChart()
2987 - cone - An array of points which are on the in-edges for point p
2988 
2989   Output Parameter:
2990 
2991   Note:
2992   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
2993 
2994   Level: beginner
2995 
2996 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
2997 @*/
2998 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[]) {
2999   DM_Plex *mesh = (DM_Plex *)dm->data;
3000   PetscInt pStart, pEnd;
3001   PetscInt dof, off, c;
3002 
3003   PetscFunctionBegin;
3004   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3005   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3006   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3007   if (dof) PetscValidIntPointer(cone, 3);
3008   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3009   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);
3010   for (c = 0; c < dof; ++c) {
3011     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);
3012     mesh->cones[off + c] = cone[c];
3013   }
3014   PetscFunctionReturn(0);
3015 }
3016 
3017 /*@C
3018   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3019 
3020   Not collective
3021 
3022   Input Parameters:
3023 + mesh - The DMPlex
3024 - p - The point, which must lie in the chart set with DMPlexSetChart()
3025 
3026   Output Parameter:
3027 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3028                     integer giving the prescription for cone traversal.
3029 
3030   Level: beginner
3031 
3032   Notes:
3033   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3034   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3035   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3036   with the identity.
3037 
3038   Fortran Notes:
3039   Since it returns an array, this routine is only available in Fortran 90, and you must
3040   include petsc.h90 in your code.
3041   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3042   DMPlexRestoreConeOrientation() is not needed/available in C.
3043 
3044 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3045 @*/
3046 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[]) {
3047   DM_Plex *mesh = (DM_Plex *)dm->data;
3048   PetscInt off;
3049 
3050   PetscFunctionBegin;
3051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3052   if (PetscDefined(USE_DEBUG)) {
3053     PetscInt dof;
3054     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3055     if (dof) PetscValidPointer(coneOrientation, 3);
3056   }
3057   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3058 
3059   *coneOrientation = &mesh->coneOrientations[off];
3060   PetscFunctionReturn(0);
3061 }
3062 
3063 /*@
3064   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3065 
3066   Not collective
3067 
3068   Input Parameters:
3069 + mesh - The DMPlex
3070 . p - The point, which must lie in the chart set with DMPlexSetChart()
3071 - coneOrientation - An array of orientations
3072   Output Parameter:
3073 
3074   Notes:
3075   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3076 
3077   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3078 
3079   Level: beginner
3080 
3081 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3082 @*/
3083 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[]) {
3084   DM_Plex *mesh = (DM_Plex *)dm->data;
3085   PetscInt pStart, pEnd;
3086   PetscInt dof, off, c;
3087 
3088   PetscFunctionBegin;
3089   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3090   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3091   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3092   if (dof) PetscValidIntPointer(coneOrientation, 3);
3093   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3094   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3095   for (c = 0; c < dof; ++c) {
3096     PetscInt cdof, o = coneOrientation[c];
3097 
3098     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3099     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);
3100     mesh->coneOrientations[off + c] = o;
3101   }
3102   PetscFunctionReturn(0);
3103 }
3104 
3105 /*@
3106   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3107 
3108   Not collective
3109 
3110   Input Parameters:
3111 + mesh - The DMPlex
3112 . p - The point, which must lie in the chart set with DMPlexSetChart()
3113 . conePos - The local index in the cone where the point should be put
3114 - conePoint - The mesh point to insert
3115 
3116   Level: beginner
3117 
3118 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3119 @*/
3120 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint) {
3121   DM_Plex *mesh = (DM_Plex *)dm->data;
3122   PetscInt pStart, pEnd;
3123   PetscInt dof, off;
3124 
3125   PetscFunctionBegin;
3126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3127   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3128   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);
3129   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);
3130   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3131   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3132   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);
3133   mesh->cones[off + conePos] = conePoint;
3134   PetscFunctionReturn(0);
3135 }
3136 
3137 /*@
3138   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3139 
3140   Not collective
3141 
3142   Input Parameters:
3143 + mesh - The DMPlex
3144 . p - The point, which must lie in the chart set with DMPlexSetChart()
3145 . conePos - The local index in the cone where the point should be put
3146 - coneOrientation - The point orientation to insert
3147 
3148   Level: beginner
3149 
3150   Notes:
3151   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3152 
3153 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3154 @*/
3155 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation) {
3156   DM_Plex *mesh = (DM_Plex *)dm->data;
3157   PetscInt pStart, pEnd;
3158   PetscInt dof, off;
3159 
3160   PetscFunctionBegin;
3161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3162   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3163   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);
3164   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3165   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3166   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);
3167   mesh->coneOrientations[off + conePos] = coneOrientation;
3168   PetscFunctionReturn(0);
3169 }
3170 
3171 /*@
3172   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3173 
3174   Not collective
3175 
3176   Input Parameters:
3177 + mesh - The DMPlex
3178 - p - The point, which must lie in the chart set with DMPlexSetChart()
3179 
3180   Output Parameter:
3181 . size - The support size for point p
3182 
3183   Level: beginner
3184 
3185 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3186 @*/
3187 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size) {
3188   DM_Plex *mesh = (DM_Plex *)dm->data;
3189 
3190   PetscFunctionBegin;
3191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3192   PetscValidIntPointer(size, 3);
3193   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3194   PetscFunctionReturn(0);
3195 }
3196 
3197 /*@
3198   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3199 
3200   Not collective
3201 
3202   Input Parameters:
3203 + mesh - The DMPlex
3204 . p - The point, which must lie in the chart set with DMPlexSetChart()
3205 - size - The support size for point p
3206 
3207   Output Parameter:
3208 
3209   Note:
3210   This should be called after DMPlexSetChart().
3211 
3212   Level: beginner
3213 
3214 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3215 @*/
3216 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size) {
3217   DM_Plex *mesh = (DM_Plex *)dm->data;
3218 
3219   PetscFunctionBegin;
3220   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3221   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3222   PetscFunctionReturn(0);
3223 }
3224 
3225 /*@C
3226   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3227 
3228   Not collective
3229 
3230   Input Parameters:
3231 + mesh - The DMPlex
3232 - p - The point, which must lie in the chart set with DMPlexSetChart()
3233 
3234   Output Parameter:
3235 . support - An array of points which are on the out-edges for point p
3236 
3237   Level: beginner
3238 
3239   Fortran Notes:
3240   Since it returns an array, this routine is only available in Fortran 90, and you must
3241   include petsc.h90 in your code.
3242   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3243   DMPlexRestoreSupport() is not needed/available in C.
3244 
3245 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3246 @*/
3247 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[]) {
3248   DM_Plex *mesh = (DM_Plex *)dm->data;
3249   PetscInt off;
3250 
3251   PetscFunctionBegin;
3252   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3253   PetscValidPointer(support, 3);
3254   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3255   *support = &mesh->supports[off];
3256   PetscFunctionReturn(0);
3257 }
3258 
3259 /*@
3260   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3261 
3262   Not collective
3263 
3264   Input Parameters:
3265 + mesh - The DMPlex
3266 . p - The point, which must lie in the chart set with DMPlexSetChart()
3267 - support - An array of points which are on the out-edges for point p
3268 
3269   Output Parameter:
3270 
3271   Note:
3272   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3273 
3274   Level: beginner
3275 
3276 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3277 @*/
3278 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[]) {
3279   DM_Plex *mesh = (DM_Plex *)dm->data;
3280   PetscInt pStart, pEnd;
3281   PetscInt dof, off, c;
3282 
3283   PetscFunctionBegin;
3284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3285   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3286   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3287   if (dof) PetscValidIntPointer(support, 3);
3288   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3289   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);
3290   for (c = 0; c < dof; ++c) {
3291     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);
3292     mesh->supports[off + c] = support[c];
3293   }
3294   PetscFunctionReturn(0);
3295 }
3296 
3297 /*@
3298   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3299 
3300   Not collective
3301 
3302   Input Parameters:
3303 + mesh - The DMPlex
3304 . p - The point, which must lie in the chart set with DMPlexSetChart()
3305 . supportPos - The local index in the cone where the point should be put
3306 - supportPoint - The mesh point to insert
3307 
3308   Level: beginner
3309 
3310 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3311 @*/
3312 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint) {
3313   DM_Plex *mesh = (DM_Plex *)dm->data;
3314   PetscInt pStart, pEnd;
3315   PetscInt dof, off;
3316 
3317   PetscFunctionBegin;
3318   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3319   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3320   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3321   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3322   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);
3323   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);
3324   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);
3325   mesh->supports[off + supportPos] = supportPoint;
3326   PetscFunctionReturn(0);
3327 }
3328 
3329 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3330 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o) {
3331   switch (ct) {
3332   case DM_POLYTOPE_SEGMENT:
3333     if (o == -1) return -2;
3334     break;
3335   case DM_POLYTOPE_TRIANGLE:
3336     if (o == -3) return -1;
3337     if (o == -2) return -3;
3338     if (o == -1) return -2;
3339     break;
3340   case DM_POLYTOPE_QUADRILATERAL:
3341     if (o == -4) return -2;
3342     if (o == -3) return -1;
3343     if (o == -2) return -4;
3344     if (o == -1) return -3;
3345     break;
3346   default: return o;
3347   }
3348   return o;
3349 }
3350 
3351 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3352 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o) {
3353   switch (ct) {
3354   case DM_POLYTOPE_SEGMENT:
3355     if ((o == -2) || (o == 1)) return -1;
3356     if (o == -1) return 0;
3357     break;
3358   case DM_POLYTOPE_TRIANGLE:
3359     if (o == -3) return -2;
3360     if (o == -2) return -1;
3361     if (o == -1) return -3;
3362     break;
3363   case DM_POLYTOPE_QUADRILATERAL:
3364     if (o == -4) return -2;
3365     if (o == -3) return -1;
3366     if (o == -2) return -4;
3367     if (o == -1) return -3;
3368     break;
3369   default: return o;
3370   }
3371   return o;
3372 }
3373 
3374 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3375 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm) {
3376   PetscInt pStart, pEnd, p;
3377 
3378   PetscFunctionBegin;
3379   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3380   for (p = pStart; p < pEnd; ++p) {
3381     const PetscInt *cone, *ornt;
3382     PetscInt        coneSize, c;
3383 
3384     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3385     PetscCall(DMPlexGetCone(dm, p, &cone));
3386     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3387     for (c = 0; c < coneSize; ++c) {
3388       DMPolytopeType ct;
3389       const PetscInt o = ornt[c];
3390 
3391       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3392       switch (ct) {
3393       case DM_POLYTOPE_SEGMENT:
3394         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3395         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3396         break;
3397       case DM_POLYTOPE_TRIANGLE:
3398         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3399         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3400         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3401         break;
3402       case DM_POLYTOPE_QUADRILATERAL:
3403         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3404         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3405         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3406         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3407         break;
3408       default: break;
3409       }
3410     }
3411   }
3412   PetscFunctionReturn(0);
3413 }
3414 
3415 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3416   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3417   PetscInt       *closure;
3418   const PetscInt *tmp = NULL, *tmpO = NULL;
3419   PetscInt        off = 0, tmpSize, t;
3420 
3421   PetscFunctionBeginHot;
3422   if (ornt) {
3423     PetscCall(DMPlexGetCellType(dm, p, &ct));
3424     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3425   }
3426   if (*points) {
3427     closure = *points;
3428   } else {
3429     PetscInt maxConeSize, maxSupportSize;
3430     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3431     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3432   }
3433   if (useCone) {
3434     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3435     PetscCall(DMPlexGetCone(dm, p, &tmp));
3436     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3437   } else {
3438     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3439     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3440   }
3441   if (ct == DM_POLYTOPE_UNKNOWN) {
3442     closure[off++] = p;
3443     closure[off++] = 0;
3444     for (t = 0; t < tmpSize; ++t) {
3445       closure[off++] = tmp[t];
3446       closure[off++] = tmpO ? tmpO[t] : 0;
3447     }
3448   } else {
3449     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3450 
3451     /* We assume that cells with a valid type have faces with a valid type */
3452     closure[off++] = p;
3453     closure[off++] = ornt;
3454     for (t = 0; t < tmpSize; ++t) {
3455       DMPolytopeType ft;
3456 
3457       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3458       closure[off++] = tmp[arr[t]];
3459       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3460     }
3461   }
3462   if (numPoints) *numPoints = tmpSize + 1;
3463   if (points) *points = closure;
3464   PetscFunctionReturn(0);
3465 }
3466 
3467 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3468 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points) {
3469   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3470   const PetscInt *cone, *ornt;
3471   PetscInt       *pts, *closure = NULL;
3472   DMPolytopeType  ft;
3473   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3474   PetscInt        dim, coneSize, c, d, clSize, cl;
3475 
3476   PetscFunctionBeginHot;
3477   PetscCall(DMGetDimension(dm, &dim));
3478   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3479   PetscCall(DMPlexGetCone(dm, point, &cone));
3480   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3481   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3482   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3483   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3484   maxSize       = PetscMax(coneSeries, supportSeries);
3485   if (*points) {
3486     pts = *points;
3487   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3488   c        = 0;
3489   pts[c++] = point;
3490   pts[c++] = o;
3491   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3492   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3493   for (cl = 0; cl < clSize * 2; cl += 2) {
3494     pts[c++] = closure[cl];
3495     pts[c++] = closure[cl + 1];
3496   }
3497   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3498   for (cl = 0; cl < clSize * 2; cl += 2) {
3499     pts[c++] = closure[cl];
3500     pts[c++] = closure[cl + 1];
3501   }
3502   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3503   for (d = 2; d < coneSize; ++d) {
3504     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3505     pts[c++] = cone[arr[d * 2 + 0]];
3506     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3507   }
3508   if (dim >= 3) {
3509     for (d = 2; d < coneSize; ++d) {
3510       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3511       const PetscInt *fcone, *fornt;
3512       PetscInt        fconeSize, fc, i;
3513 
3514       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3515       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3516       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3517       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3518       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3519       for (fc = 0; fc < fconeSize; ++fc) {
3520         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3521         const PetscInt co = farr[fc * 2 + 1];
3522 
3523         for (i = 0; i < c; i += 2)
3524           if (pts[i] == cp) break;
3525         if (i == c) {
3526           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3527           pts[c++] = cp;
3528           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3529         }
3530       }
3531     }
3532   }
3533   *numPoints = c / 2;
3534   *points    = pts;
3535   PetscFunctionReturn(0);
3536 }
3537 
3538 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3539   DMPolytopeType ct;
3540   PetscInt      *closure, *fifo;
3541   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3542   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3543   PetscInt       depth, maxSize;
3544 
3545   PetscFunctionBeginHot;
3546   PetscCall(DMPlexGetDepth(dm, &depth));
3547   if (depth == 1) {
3548     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3549     PetscFunctionReturn(0);
3550   }
3551   PetscCall(DMPlexGetCellType(dm, p, &ct));
3552   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3553   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3554     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3555     PetscFunctionReturn(0);
3556   }
3557   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3558   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3559   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3560   maxSize       = PetscMax(coneSeries, supportSeries);
3561   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3562   if (*points) {
3563     closure = *points;
3564   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3565   closure[closureSize++] = p;
3566   closure[closureSize++] = ornt;
3567   fifo[fifoSize++]       = p;
3568   fifo[fifoSize++]       = ornt;
3569   fifo[fifoSize++]       = ct;
3570   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3571   while (fifoSize - fifoStart) {
3572     const PetscInt       q    = fifo[fifoStart++];
3573     const PetscInt       o    = fifo[fifoStart++];
3574     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3575     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3576     const PetscInt      *tmp, *tmpO;
3577     PetscInt             tmpSize, t;
3578 
3579     if (PetscDefined(USE_DEBUG)) {
3580       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3581       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);
3582     }
3583     if (useCone) {
3584       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3585       PetscCall(DMPlexGetCone(dm, q, &tmp));
3586       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3587     } else {
3588       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3589       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3590       tmpO = NULL;
3591     }
3592     for (t = 0; t < tmpSize; ++t) {
3593       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3594       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3595       const PetscInt cp = tmp[ip];
3596       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3597       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3598       PetscInt       c;
3599 
3600       /* Check for duplicate */
3601       for (c = 0; c < closureSize; c += 2) {
3602         if (closure[c] == cp) break;
3603       }
3604       if (c == closureSize) {
3605         closure[closureSize++] = cp;
3606         closure[closureSize++] = co;
3607         fifo[fifoSize++]       = cp;
3608         fifo[fifoSize++]       = co;
3609         fifo[fifoSize++]       = ct;
3610       }
3611     }
3612   }
3613   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3614   if (numPoints) *numPoints = closureSize / 2;
3615   if (points) *points = closure;
3616   PetscFunctionReturn(0);
3617 }
3618 
3619 /*@C
3620   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3621 
3622   Not collective
3623 
3624   Input Parameters:
3625 + dm      - The DMPlex
3626 . p       - The mesh point
3627 - useCone - PETSC_TRUE for the closure, otherwise return the star
3628 
3629   Input/Output Parameter:
3630 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3631            if NULL on input, internal storage will be returned, otherwise the provided array is used
3632 
3633   Output Parameter:
3634 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3635 
3636   Note:
3637   If using internal storage (points is NULL on input), each call overwrites the last output.
3638 
3639   Fortran Note:
3640   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3641 
3642   Level: beginner
3643 
3644 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3645 @*/
3646 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3647   PetscFunctionBeginHot;
3648   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3649   if (numPoints) PetscValidIntPointer(numPoints, 4);
3650   if (points) PetscValidPointer(points, 5);
3651   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3652   PetscFunctionReturn(0);
3653 }
3654 
3655 /*@C
3656   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3657 
3658   Not collective
3659 
3660   Input Parameters:
3661 + dm        - The DMPlex
3662 . p         - The mesh point
3663 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3664 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3665 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3666 
3667   Note:
3668   If not using internal storage (points is not NULL on input), this call is unnecessary
3669 
3670   Level: beginner
3671 
3672 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3673 @*/
3674 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3675   PetscFunctionBeginHot;
3676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3677   if (numPoints) *numPoints = 0;
3678   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3679   PetscFunctionReturn(0);
3680 }
3681 
3682 /*@
3683   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3684 
3685   Not collective
3686 
3687   Input Parameter:
3688 . mesh - The DMPlex
3689 
3690   Output Parameters:
3691 + maxConeSize - The maximum number of in-edges
3692 - maxSupportSize - The maximum number of out-edges
3693 
3694   Level: beginner
3695 
3696 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3697 @*/
3698 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize) {
3699   DM_Plex *mesh = (DM_Plex *)dm->data;
3700 
3701   PetscFunctionBegin;
3702   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3703   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3704   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3705   PetscFunctionReturn(0);
3706 }
3707 
3708 PetscErrorCode DMSetUp_Plex(DM dm) {
3709   DM_Plex *mesh = (DM_Plex *)dm->data;
3710   PetscInt size, maxSupportSize;
3711 
3712   PetscFunctionBegin;
3713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3714   PetscCall(PetscSectionSetUp(mesh->coneSection));
3715   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3716   PetscCall(PetscMalloc1(size, &mesh->cones));
3717   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3718   PetscCall(PetscLogObjectMemory((PetscObject)dm, size * 2 * sizeof(PetscInt)));
3719   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3720   if (maxSupportSize) {
3721     PetscCall(PetscSectionSetUp(mesh->supportSection));
3722     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3723     PetscCall(PetscMalloc1(size, &mesh->supports));
3724     PetscCall(PetscLogObjectMemory((PetscObject)dm, size * sizeof(PetscInt)));
3725   }
3726   PetscFunctionReturn(0);
3727 }
3728 
3729 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm) {
3730   PetscFunctionBegin;
3731   if (subdm) PetscCall(DMClone(dm, subdm));
3732   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3733   if (subdm) (*subdm)->useNatural = dm->useNatural;
3734   if (dm->useNatural && dm->sfMigration) {
3735     PetscSF sfNatural;
3736 
3737     (*subdm)->sfMigration = dm->sfMigration;
3738     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3739     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3740     (*subdm)->sfNatural = sfNatural;
3741   }
3742   PetscFunctionReturn(0);
3743 }
3744 
3745 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm) {
3746   PetscInt i = 0;
3747 
3748   PetscFunctionBegin;
3749   PetscCall(DMClone(dms[0], superdm));
3750   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3751   (*superdm)->useNatural = PETSC_FALSE;
3752   for (i = 0; i < len; i++) {
3753     if (dms[i]->useNatural && dms[i]->sfMigration) {
3754       PetscSF sfNatural;
3755 
3756       (*superdm)->sfMigration = dms[i]->sfMigration;
3757       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3758       (*superdm)->useNatural = PETSC_TRUE;
3759       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3760       (*superdm)->sfNatural = sfNatural;
3761       break;
3762     }
3763   }
3764   PetscFunctionReturn(0);
3765 }
3766 
3767 /*@
3768   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3769 
3770   Not collective
3771 
3772   Input Parameter:
3773 . mesh - The DMPlex
3774 
3775   Output Parameter:
3776 
3777   Note:
3778   This should be called after all calls to DMPlexSetCone()
3779 
3780   Level: beginner
3781 
3782 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3783 @*/
3784 PetscErrorCode DMPlexSymmetrize(DM dm) {
3785   DM_Plex  *mesh = (DM_Plex *)dm->data;
3786   PetscInt *offsets;
3787   PetscInt  supportSize;
3788   PetscInt  pStart, pEnd, p;
3789 
3790   PetscFunctionBegin;
3791   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3792   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3793   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3794   /* Calculate support sizes */
3795   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3796   for (p = pStart; p < pEnd; ++p) {
3797     PetscInt dof, off, c;
3798 
3799     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3800     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3801     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3802   }
3803   PetscCall(PetscSectionSetUp(mesh->supportSection));
3804   /* Calculate supports */
3805   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3806   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3807   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3808   for (p = pStart; p < pEnd; ++p) {
3809     PetscInt dof, off, c;
3810 
3811     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3812     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3813     for (c = off; c < off + dof; ++c) {
3814       const PetscInt q = mesh->cones[c];
3815       PetscInt       offS;
3816 
3817       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3818 
3819       mesh->supports[offS + offsets[q]] = p;
3820       ++offsets[q];
3821     }
3822   }
3823   PetscCall(PetscFree(offsets));
3824   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
3825   PetscFunctionReturn(0);
3826 }
3827 
3828 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd) {
3829   IS stratumIS;
3830 
3831   PetscFunctionBegin;
3832   if (pStart >= pEnd) PetscFunctionReturn(0);
3833   if (PetscDefined(USE_DEBUG)) {
3834     PetscInt  qStart, qEnd, numLevels, level;
3835     PetscBool overlap = PETSC_FALSE;
3836     PetscCall(DMLabelGetNumValues(label, &numLevels));
3837     for (level = 0; level < numLevels; level++) {
3838       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3839       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
3840         overlap = PETSC_TRUE;
3841         break;
3842       }
3843     }
3844     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);
3845   }
3846   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
3847   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3848   PetscCall(ISDestroy(&stratumIS));
3849   PetscFunctionReturn(0);
3850 }
3851 
3852 /*@
3853   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3854   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3855   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3856   the DAG.
3857 
3858   Collective on dm
3859 
3860   Input Parameter:
3861 . mesh - The DMPlex
3862 
3863   Output Parameter:
3864 
3865   Notes:
3866   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3867   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3868   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3869   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3870   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3871 
3872   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3873   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3874   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
3875   to interpolate only that one (e0), so that
3876 $  cone(c0) = {e0, v2}
3877 $  cone(e0) = {v0, v1}
3878   If DMPlexStratify() is run on this mesh, it will give depths
3879 $  depth 0 = {v0, v1, v2}
3880 $  depth 1 = {e0, c0}
3881   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3882 
3883   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3884 
3885   Level: beginner
3886 
3887 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3888 @*/
3889 PetscErrorCode DMPlexStratify(DM dm) {
3890   DM_Plex *mesh = (DM_Plex *)dm->data;
3891   DMLabel  label;
3892   PetscInt pStart, pEnd, p;
3893   PetscInt numRoots = 0, numLeaves = 0;
3894 
3895   PetscFunctionBegin;
3896   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3897   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
3898 
3899   /* Create depth label */
3900   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3901   PetscCall(DMCreateLabel(dm, "depth"));
3902   PetscCall(DMPlexGetDepthLabel(dm, &label));
3903 
3904   {
3905     /* Initialize roots and count leaves */
3906     PetscInt sMin = PETSC_MAX_INT;
3907     PetscInt sMax = PETSC_MIN_INT;
3908     PetscInt coneSize, supportSize;
3909 
3910     for (p = pStart; p < pEnd; ++p) {
3911       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3912       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3913       if (!coneSize && supportSize) {
3914         sMin = PetscMin(p, sMin);
3915         sMax = PetscMax(p, sMax);
3916         ++numRoots;
3917       } else if (!supportSize && coneSize) {
3918         ++numLeaves;
3919       } else if (!supportSize && !coneSize) {
3920         /* Isolated points */
3921         sMin = PetscMin(p, sMin);
3922         sMax = PetscMax(p, sMax);
3923       }
3924     }
3925     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
3926   }
3927 
3928   if (numRoots + numLeaves == (pEnd - pStart)) {
3929     PetscInt sMin = PETSC_MAX_INT;
3930     PetscInt sMax = PETSC_MIN_INT;
3931     PetscInt coneSize, supportSize;
3932 
3933     for (p = pStart; p < pEnd; ++p) {
3934       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3935       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3936       if (!supportSize && coneSize) {
3937         sMin = PetscMin(p, sMin);
3938         sMax = PetscMax(p, sMax);
3939       }
3940     }
3941     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
3942   } else {
3943     PetscInt level = 0;
3944     PetscInt qStart, qEnd, q;
3945 
3946     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3947     while (qEnd > qStart) {
3948       PetscInt sMin = PETSC_MAX_INT;
3949       PetscInt sMax = PETSC_MIN_INT;
3950 
3951       for (q = qStart; q < qEnd; ++q) {
3952         const PetscInt *support;
3953         PetscInt        supportSize, s;
3954 
3955         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
3956         PetscCall(DMPlexGetSupport(dm, q, &support));
3957         for (s = 0; s < supportSize; ++s) {
3958           sMin = PetscMin(support[s], sMin);
3959           sMax = PetscMax(support[s], sMax);
3960         }
3961       }
3962       PetscCall(DMLabelGetNumValues(label, &level));
3963       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
3964       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3965     }
3966   }
3967   { /* just in case there is an empty process */
3968     PetscInt numValues, maxValues = 0, v;
3969 
3970     PetscCall(DMLabelGetNumValues(label, &numValues));
3971     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
3972     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
3973   }
3974   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
3975   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
3976   PetscFunctionReturn(0);
3977 }
3978 
3979 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt) {
3980   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
3981   PetscInt       dim, depth, pheight, coneSize;
3982 
3983   PetscFunctionBeginHot;
3984   PetscCall(DMGetDimension(dm, &dim));
3985   PetscCall(DMPlexGetDepth(dm, &depth));
3986   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3987   pheight = depth - pdepth;
3988   if (depth <= 1) {
3989     switch (pdepth) {
3990     case 0: ct = DM_POLYTOPE_POINT; break;
3991     case 1:
3992       switch (coneSize) {
3993       case 2: ct = DM_POLYTOPE_SEGMENT; break;
3994       case 3: ct = DM_POLYTOPE_TRIANGLE; break;
3995       case 4:
3996         switch (dim) {
3997         case 2: ct = DM_POLYTOPE_QUADRILATERAL; break;
3998         case 3: ct = DM_POLYTOPE_TETRAHEDRON; break;
3999         default: break;
4000         }
4001         break;
4002       case 5: ct = DM_POLYTOPE_PYRAMID; break;
4003       case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR; break;
4004       case 8: ct = DM_POLYTOPE_HEXAHEDRON; break;
4005       default: break;
4006       }
4007     }
4008   } else {
4009     if (pdepth == 0) {
4010       ct = DM_POLYTOPE_POINT;
4011     } else if (pheight == 0) {
4012       switch (dim) {
4013       case 1:
4014         switch (coneSize) {
4015         case 2: ct = DM_POLYTOPE_SEGMENT; break;
4016         default: break;
4017         }
4018         break;
4019       case 2:
4020         switch (coneSize) {
4021         case 3: ct = DM_POLYTOPE_TRIANGLE; break;
4022         case 4: ct = DM_POLYTOPE_QUADRILATERAL; break;
4023         default: break;
4024         }
4025         break;
4026       case 3:
4027         switch (coneSize) {
4028         case 4: ct = DM_POLYTOPE_TETRAHEDRON; break;
4029         case 5: {
4030           const PetscInt *cone;
4031           PetscInt        faceConeSize;
4032 
4033           PetscCall(DMPlexGetCone(dm, p, &cone));
4034           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4035           switch (faceConeSize) {
4036           case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR; break;
4037           case 4: ct = DM_POLYTOPE_PYRAMID; break;
4038           }
4039         } break;
4040         case 6: ct = DM_POLYTOPE_HEXAHEDRON; break;
4041         default: break;
4042         }
4043         break;
4044       default: break;
4045       }
4046     } else if (pheight > 0) {
4047       switch (coneSize) {
4048       case 2: ct = DM_POLYTOPE_SEGMENT; break;
4049       case 3: ct = DM_POLYTOPE_TRIANGLE; break;
4050       case 4: ct = DM_POLYTOPE_QUADRILATERAL; break;
4051       default: break;
4052       }
4053     }
4054   }
4055   *pt = ct;
4056   PetscFunctionReturn(0);
4057 }
4058 
4059 /*@
4060   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4061 
4062   Collective on dm
4063 
4064   Input Parameter:
4065 . mesh - The DMPlex
4066 
4067   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4068 
4069   Level: developer
4070 
4071   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4072   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4073   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4074 
4075 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4076 @*/
4077 PetscErrorCode DMPlexComputeCellTypes(DM dm) {
4078   DM_Plex *mesh;
4079   DMLabel  ctLabel;
4080   PetscInt pStart, pEnd, p;
4081 
4082   PetscFunctionBegin;
4083   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4084   mesh = (DM_Plex *)dm->data;
4085   PetscCall(DMCreateLabel(dm, "celltype"));
4086   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4087   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4088   for (p = pStart; p < pEnd; ++p) {
4089     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4090     PetscInt       pdepth;
4091 
4092     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4093     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4094     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4095     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4096   }
4097   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4098   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4099   PetscFunctionReturn(0);
4100 }
4101 
4102 /*@C
4103   DMPlexGetJoin - Get an array for the join of the set of points
4104 
4105   Not Collective
4106 
4107   Input Parameters:
4108 + dm - The DMPlex object
4109 . numPoints - The number of input points for the join
4110 - points - The input points
4111 
4112   Output Parameters:
4113 + numCoveredPoints - The number of points in the join
4114 - coveredPoints - The points in the join
4115 
4116   Level: intermediate
4117 
4118   Note: Currently, this is restricted to a single level join
4119 
4120   Fortran Notes:
4121   Since it returns an array, this routine is only available in Fortran 90, and you must
4122   include petsc.h90 in your code.
4123 
4124   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4125 
4126 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4127 @*/
4128 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4129   DM_Plex  *mesh = (DM_Plex *)dm->data;
4130   PetscInt *join[2];
4131   PetscInt  joinSize, i = 0;
4132   PetscInt  dof, off, p, c, m;
4133   PetscInt  maxSupportSize;
4134 
4135   PetscFunctionBegin;
4136   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4137   PetscValidIntPointer(points, 3);
4138   PetscValidIntPointer(numCoveredPoints, 4);
4139   PetscValidPointer(coveredPoints, 5);
4140   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4141   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4142   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4143   /* Copy in support of first point */
4144   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4145   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4146   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4147   /* Check each successive support */
4148   for (p = 1; p < numPoints; ++p) {
4149     PetscInt newJoinSize = 0;
4150 
4151     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4152     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4153     for (c = 0; c < dof; ++c) {
4154       const PetscInt point = mesh->supports[off + c];
4155 
4156       for (m = 0; m < joinSize; ++m) {
4157         if (point == join[i][m]) {
4158           join[1 - i][newJoinSize++] = point;
4159           break;
4160         }
4161       }
4162     }
4163     joinSize = newJoinSize;
4164     i        = 1 - i;
4165   }
4166   *numCoveredPoints = joinSize;
4167   *coveredPoints    = join[i];
4168   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4169   PetscFunctionReturn(0);
4170 }
4171 
4172 /*@C
4173   DMPlexRestoreJoin - Restore an array for the join of the set of points
4174 
4175   Not Collective
4176 
4177   Input Parameters:
4178 + dm - The DMPlex object
4179 . numPoints - The number of input points for the join
4180 - points - The input points
4181 
4182   Output Parameters:
4183 + numCoveredPoints - The number of points in the join
4184 - coveredPoints - The points in the join
4185 
4186   Fortran Notes:
4187   Since it returns an array, this routine is only available in Fortran 90, and you must
4188   include petsc.h90 in your code.
4189 
4190   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4191 
4192   Level: intermediate
4193 
4194 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4195 @*/
4196 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4197   PetscFunctionBegin;
4198   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4199   if (points) PetscValidIntPointer(points, 3);
4200   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4201   PetscValidPointer(coveredPoints, 5);
4202   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4203   if (numCoveredPoints) *numCoveredPoints = 0;
4204   PetscFunctionReturn(0);
4205 }
4206 
4207 /*@C
4208   DMPlexGetFullJoin - Get an array for the join of the set of points
4209 
4210   Not Collective
4211 
4212   Input Parameters:
4213 + dm - The DMPlex object
4214 . numPoints - The number of input points for the join
4215 - points - The input points
4216 
4217   Output Parameters:
4218 + numCoveredPoints - The number of points in the join
4219 - coveredPoints - The points in the join
4220 
4221   Fortran Notes:
4222   Since it returns an array, this routine is only available in Fortran 90, and you must
4223   include petsc.h90 in your code.
4224 
4225   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4226 
4227   Level: intermediate
4228 
4229 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4230 @*/
4231 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4232   PetscInt *offsets, **closures;
4233   PetscInt *join[2];
4234   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4235   PetscInt  p, d, c, m, ms;
4236 
4237   PetscFunctionBegin;
4238   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4239   PetscValidIntPointer(points, 3);
4240   PetscValidIntPointer(numCoveredPoints, 4);
4241   PetscValidPointer(coveredPoints, 5);
4242 
4243   PetscCall(DMPlexGetDepth(dm, &depth));
4244   PetscCall(PetscCalloc1(numPoints, &closures));
4245   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4246   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4247   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4248   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4249   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4250 
4251   for (p = 0; p < numPoints; ++p) {
4252     PetscInt closureSize;
4253 
4254     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4255 
4256     offsets[p * (depth + 2) + 0] = 0;
4257     for (d = 0; d < depth + 1; ++d) {
4258       PetscInt pStart, pEnd, i;
4259 
4260       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4261       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4262         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4263           offsets[p * (depth + 2) + d + 1] = i;
4264           break;
4265         }
4266       }
4267       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4268     }
4269     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);
4270   }
4271   for (d = 0; d < depth + 1; ++d) {
4272     PetscInt dof;
4273 
4274     /* Copy in support of first point */
4275     dof = offsets[d + 1] - offsets[d];
4276     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4277     /* Check each successive cone */
4278     for (p = 1; p < numPoints && joinSize; ++p) {
4279       PetscInt newJoinSize = 0;
4280 
4281       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4282       for (c = 0; c < dof; ++c) {
4283         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4284 
4285         for (m = 0; m < joinSize; ++m) {
4286           if (point == join[i][m]) {
4287             join[1 - i][newJoinSize++] = point;
4288             break;
4289           }
4290         }
4291       }
4292       joinSize = newJoinSize;
4293       i        = 1 - i;
4294     }
4295     if (joinSize) break;
4296   }
4297   *numCoveredPoints = joinSize;
4298   *coveredPoints    = join[i];
4299   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4300   PetscCall(PetscFree(closures));
4301   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4302   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4303   PetscFunctionReturn(0);
4304 }
4305 
4306 /*@C
4307   DMPlexGetMeet - Get an array for the meet of the set of points
4308 
4309   Not Collective
4310 
4311   Input Parameters:
4312 + dm - The DMPlex object
4313 . numPoints - The number of input points for the meet
4314 - points - The input points
4315 
4316   Output Parameters:
4317 + numCoveredPoints - The number of points in the meet
4318 - coveredPoints - The points in the meet
4319 
4320   Level: intermediate
4321 
4322   Note: Currently, this is restricted to a single level meet
4323 
4324   Fortran Notes:
4325   Since it returns an array, this routine is only available in Fortran 90, and you must
4326   include petsc.h90 in your code.
4327 
4328   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4329 
4330 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4331 @*/
4332 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints) {
4333   DM_Plex  *mesh = (DM_Plex *)dm->data;
4334   PetscInt *meet[2];
4335   PetscInt  meetSize, i = 0;
4336   PetscInt  dof, off, p, c, m;
4337   PetscInt  maxConeSize;
4338 
4339   PetscFunctionBegin;
4340   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4341   PetscValidIntPointer(points, 3);
4342   PetscValidIntPointer(numCoveringPoints, 4);
4343   PetscValidPointer(coveringPoints, 5);
4344   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4345   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4346   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4347   /* Copy in cone of first point */
4348   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4349   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4350   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4351   /* Check each successive cone */
4352   for (p = 1; p < numPoints; ++p) {
4353     PetscInt newMeetSize = 0;
4354 
4355     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4356     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4357     for (c = 0; c < dof; ++c) {
4358       const PetscInt point = mesh->cones[off + c];
4359 
4360       for (m = 0; m < meetSize; ++m) {
4361         if (point == meet[i][m]) {
4362           meet[1 - i][newMeetSize++] = point;
4363           break;
4364         }
4365       }
4366     }
4367     meetSize = newMeetSize;
4368     i        = 1 - i;
4369   }
4370   *numCoveringPoints = meetSize;
4371   *coveringPoints    = meet[i];
4372   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4373   PetscFunctionReturn(0);
4374 }
4375 
4376 /*@C
4377   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4378 
4379   Not Collective
4380 
4381   Input Parameters:
4382 + dm - The DMPlex object
4383 . numPoints - The number of input points for the meet
4384 - points - The input points
4385 
4386   Output Parameters:
4387 + numCoveredPoints - The number of points in the meet
4388 - coveredPoints - The points in the meet
4389 
4390   Level: intermediate
4391 
4392   Fortran Notes:
4393   Since it returns an array, this routine is only available in Fortran 90, and you must
4394   include petsc.h90 in your code.
4395 
4396   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4397 
4398 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4399 @*/
4400 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4401   PetscFunctionBegin;
4402   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4403   if (points) PetscValidIntPointer(points, 3);
4404   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4405   PetscValidPointer(coveredPoints, 5);
4406   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4407   if (numCoveredPoints) *numCoveredPoints = 0;
4408   PetscFunctionReturn(0);
4409 }
4410 
4411 /*@C
4412   DMPlexGetFullMeet - Get an array for the meet of the set of points
4413 
4414   Not Collective
4415 
4416   Input Parameters:
4417 + dm - The DMPlex object
4418 . numPoints - The number of input points for the meet
4419 - points - The input points
4420 
4421   Output Parameters:
4422 + numCoveredPoints - The number of points in the meet
4423 - coveredPoints - The points in the meet
4424 
4425   Level: intermediate
4426 
4427   Fortran Notes:
4428   Since it returns an array, this routine is only available in Fortran 90, and you must
4429   include petsc.h90 in your code.
4430 
4431   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4432 
4433 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4434 @*/
4435 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4436   PetscInt *offsets, **closures;
4437   PetscInt *meet[2];
4438   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4439   PetscInt  p, h, c, m, mc;
4440 
4441   PetscFunctionBegin;
4442   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4443   PetscValidIntPointer(points, 3);
4444   PetscValidIntPointer(numCoveredPoints, 4);
4445   PetscValidPointer(coveredPoints, 5);
4446 
4447   PetscCall(DMPlexGetDepth(dm, &height));
4448   PetscCall(PetscMalloc1(numPoints, &closures));
4449   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4450   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4451   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4452   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4453   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4454 
4455   for (p = 0; p < numPoints; ++p) {
4456     PetscInt closureSize;
4457 
4458     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4459 
4460     offsets[p * (height + 2) + 0] = 0;
4461     for (h = 0; h < height + 1; ++h) {
4462       PetscInt pStart, pEnd, i;
4463 
4464       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4465       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4466         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4467           offsets[p * (height + 2) + h + 1] = i;
4468           break;
4469         }
4470       }
4471       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4472     }
4473     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);
4474   }
4475   for (h = 0; h < height + 1; ++h) {
4476     PetscInt dof;
4477 
4478     /* Copy in cone of first point */
4479     dof = offsets[h + 1] - offsets[h];
4480     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4481     /* Check each successive cone */
4482     for (p = 1; p < numPoints && meetSize; ++p) {
4483       PetscInt newMeetSize = 0;
4484 
4485       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4486       for (c = 0; c < dof; ++c) {
4487         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4488 
4489         for (m = 0; m < meetSize; ++m) {
4490           if (point == meet[i][m]) {
4491             meet[1 - i][newMeetSize++] = point;
4492             break;
4493           }
4494         }
4495       }
4496       meetSize = newMeetSize;
4497       i        = 1 - i;
4498     }
4499     if (meetSize) break;
4500   }
4501   *numCoveredPoints = meetSize;
4502   *coveredPoints    = meet[i];
4503   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4504   PetscCall(PetscFree(closures));
4505   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4506   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4507   PetscFunctionReturn(0);
4508 }
4509 
4510 /*@C
4511   DMPlexEqual - Determine if two DMs have the same topology
4512 
4513   Not Collective
4514 
4515   Input Parameters:
4516 + dmA - A DMPlex object
4517 - dmB - A DMPlex object
4518 
4519   Output Parameters:
4520 . equal - PETSC_TRUE if the topologies are identical
4521 
4522   Level: intermediate
4523 
4524   Notes:
4525   We are not solving graph isomorphism, so we do not permutation.
4526 
4527 .seealso: `DMPlexGetCone()`
4528 @*/
4529 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal) {
4530   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4531 
4532   PetscFunctionBegin;
4533   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4534   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4535   PetscValidBoolPointer(equal, 3);
4536 
4537   *equal = PETSC_FALSE;
4538   PetscCall(DMPlexGetDepth(dmA, &depth));
4539   PetscCall(DMPlexGetDepth(dmB, &depthB));
4540   if (depth != depthB) PetscFunctionReturn(0);
4541   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4542   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4543   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4544   for (p = pStart; p < pEnd; ++p) {
4545     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4546     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4547 
4548     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4549     PetscCall(DMPlexGetCone(dmA, p, &cone));
4550     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4551     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4552     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4553     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4554     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4555     for (c = 0; c < coneSize; ++c) {
4556       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4557       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4558     }
4559     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4560     PetscCall(DMPlexGetSupport(dmA, p, &support));
4561     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4562     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4563     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4564     for (s = 0; s < supportSize; ++s) {
4565       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4566     }
4567   }
4568   *equal = PETSC_TRUE;
4569   PetscFunctionReturn(0);
4570 }
4571 
4572 /*@C
4573   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4574 
4575   Not Collective
4576 
4577   Input Parameters:
4578 + dm         - The DMPlex
4579 . cellDim    - The cell dimension
4580 - numCorners - The number of vertices on a cell
4581 
4582   Output Parameters:
4583 . numFaceVertices - The number of vertices on a face
4584 
4585   Level: developer
4586 
4587   Notes:
4588   Of course this can only work for a restricted set of symmetric shapes
4589 
4590 .seealso: `DMPlexGetCone()`
4591 @*/
4592 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices) {
4593   MPI_Comm comm;
4594 
4595   PetscFunctionBegin;
4596   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4597   PetscValidIntPointer(numFaceVertices, 4);
4598   switch (cellDim) {
4599   case 0: *numFaceVertices = 0; break;
4600   case 1: *numFaceVertices = 1; break;
4601   case 2:
4602     switch (numCorners) {
4603     case 3:                 /* triangle */
4604       *numFaceVertices = 2; /* Edge has 2 vertices */
4605       break;
4606     case 4:                 /* quadrilateral */
4607       *numFaceVertices = 2; /* Edge has 2 vertices */
4608       break;
4609     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4610       *numFaceVertices = 3; /* Edge has 3 vertices */
4611       break;
4612     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4613       *numFaceVertices = 3; /* Edge has 3 vertices */
4614       break;
4615     default: SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4616     }
4617     break;
4618   case 3:
4619     switch (numCorners) {
4620     case 4:                 /* tetradehdron */
4621       *numFaceVertices = 3; /* Face has 3 vertices */
4622       break;
4623     case 6:                 /* tet cohesive cells */
4624       *numFaceVertices = 4; /* Face has 4 vertices */
4625       break;
4626     case 8:                 /* hexahedron */
4627       *numFaceVertices = 4; /* Face has 4 vertices */
4628       break;
4629     case 9:                 /* tet cohesive Lagrange cells */
4630       *numFaceVertices = 6; /* Face has 6 vertices */
4631       break;
4632     case 10:                /* quadratic tetrahedron */
4633       *numFaceVertices = 6; /* Face has 6 vertices */
4634       break;
4635     case 12:                /* hex cohesive Lagrange cells */
4636       *numFaceVertices = 6; /* Face has 6 vertices */
4637       break;
4638     case 18:                /* quadratic tet cohesive Lagrange cells */
4639       *numFaceVertices = 6; /* Face has 6 vertices */
4640       break;
4641     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4642       *numFaceVertices = 9; /* Face has 9 vertices */
4643       break;
4644     default: SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4645     }
4646     break;
4647   default: SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4648   }
4649   PetscFunctionReturn(0);
4650 }
4651 
4652 /*@
4653   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4654 
4655   Not Collective
4656 
4657   Input Parameter:
4658 . dm    - The DMPlex object
4659 
4660   Output Parameter:
4661 . depthLabel - The DMLabel recording point depth
4662 
4663   Level: developer
4664 
4665 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4666 @*/
4667 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel) {
4668   PetscFunctionBegin;
4669   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4670   PetscValidPointer(depthLabel, 2);
4671   *depthLabel = dm->depthLabel;
4672   PetscFunctionReturn(0);
4673 }
4674 
4675 /*@
4676   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4677 
4678   Not Collective
4679 
4680   Input Parameter:
4681 . dm    - The DMPlex object
4682 
4683   Output Parameter:
4684 . depth - The number of strata (breadth first levels) in the DAG
4685 
4686   Level: developer
4687 
4688   Notes:
4689   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4690   The point depth is described more in detail in DMPlexGetDepthStratum().
4691   An empty mesh gives -1.
4692 
4693 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4694 @*/
4695 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth) {
4696   DMLabel  label;
4697   PetscInt d = 0;
4698 
4699   PetscFunctionBegin;
4700   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4701   PetscValidIntPointer(depth, 2);
4702   PetscCall(DMPlexGetDepthLabel(dm, &label));
4703   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4704   *depth = d - 1;
4705   PetscFunctionReturn(0);
4706 }
4707 
4708 /*@
4709   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4710 
4711   Not Collective
4712 
4713   Input Parameters:
4714 + dm    - The DMPlex object
4715 - depth - The requested depth
4716 
4717   Output Parameters:
4718 + start - The first point at this depth
4719 - end   - One beyond the last point at this depth
4720 
4721   Notes:
4722   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4723   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4724   higher dimension, e.g., "edges".
4725 
4726   Level: developer
4727 
4728 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4729 @*/
4730 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end) {
4731   DMLabel  label;
4732   PetscInt pStart, pEnd;
4733 
4734   PetscFunctionBegin;
4735   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4736   if (start) {
4737     PetscValidIntPointer(start, 3);
4738     *start = 0;
4739   }
4740   if (end) {
4741     PetscValidIntPointer(end, 4);
4742     *end = 0;
4743   }
4744   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4745   if (pStart == pEnd) PetscFunctionReturn(0);
4746   if (depth < 0) {
4747     if (start) *start = pStart;
4748     if (end) *end = pEnd;
4749     PetscFunctionReturn(0);
4750   }
4751   PetscCall(DMPlexGetDepthLabel(dm, &label));
4752   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4753   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4754   PetscFunctionReturn(0);
4755 }
4756 
4757 /*@
4758   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4759 
4760   Not Collective
4761 
4762   Input Parameters:
4763 + dm     - The DMPlex object
4764 - height - The requested height
4765 
4766   Output Parameters:
4767 + start - The first point at this height
4768 - end   - One beyond the last point at this height
4769 
4770   Notes:
4771   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4772   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4773   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4774 
4775   Level: developer
4776 
4777 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4778 @*/
4779 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end) {
4780   DMLabel  label;
4781   PetscInt depth, pStart, pEnd;
4782 
4783   PetscFunctionBegin;
4784   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4785   if (start) {
4786     PetscValidIntPointer(start, 3);
4787     *start = 0;
4788   }
4789   if (end) {
4790     PetscValidIntPointer(end, 4);
4791     *end = 0;
4792   }
4793   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4794   if (pStart == pEnd) PetscFunctionReturn(0);
4795   if (height < 0) {
4796     if (start) *start = pStart;
4797     if (end) *end = pEnd;
4798     PetscFunctionReturn(0);
4799   }
4800   PetscCall(DMPlexGetDepthLabel(dm, &label));
4801   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4802   PetscCall(DMLabelGetNumValues(label, &depth));
4803   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
4804   PetscFunctionReturn(0);
4805 }
4806 
4807 /*@
4808   DMPlexGetPointDepth - Get the depth of a given point
4809 
4810   Not Collective
4811 
4812   Input Parameters:
4813 + dm    - The DMPlex object
4814 - point - The point
4815 
4816   Output Parameter:
4817 . depth - The depth of the point
4818 
4819   Level: intermediate
4820 
4821 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4822 @*/
4823 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth) {
4824   PetscFunctionBegin;
4825   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4826   PetscValidIntPointer(depth, 3);
4827   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4828   PetscFunctionReturn(0);
4829 }
4830 
4831 /*@
4832   DMPlexGetPointHeight - Get the height of a given point
4833 
4834   Not Collective
4835 
4836   Input Parameters:
4837 + dm    - The DMPlex object
4838 - point - The point
4839 
4840   Output Parameter:
4841 . height - The height of the point
4842 
4843   Level: intermediate
4844 
4845 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4846 @*/
4847 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height) {
4848   PetscInt n, pDepth;
4849 
4850   PetscFunctionBegin;
4851   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4852   PetscValidIntPointer(height, 3);
4853   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4854   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4855   *height = n - 1 - pDepth; /* DAG depth is n-1 */
4856   PetscFunctionReturn(0);
4857 }
4858 
4859 /*@
4860   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4861 
4862   Not Collective
4863 
4864   Input Parameter:
4865 . dm - The DMPlex object
4866 
4867   Output Parameter:
4868 . celltypeLabel - The DMLabel recording cell polytope type
4869 
4870   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4871   DMCreateLabel(dm, "celltype") beforehand.
4872 
4873   Level: developer
4874 
4875 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4876 @*/
4877 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel) {
4878   PetscFunctionBegin;
4879   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4880   PetscValidPointer(celltypeLabel, 2);
4881   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4882   *celltypeLabel = dm->celltypeLabel;
4883   PetscFunctionReturn(0);
4884 }
4885 
4886 /*@
4887   DMPlexGetCellType - Get the polytope type of a given cell
4888 
4889   Not Collective
4890 
4891   Input Parameters:
4892 + dm   - The DMPlex object
4893 - cell - The cell
4894 
4895   Output Parameter:
4896 . celltype - The polytope type of the cell
4897 
4898   Level: intermediate
4899 
4900 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4901 @*/
4902 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype) {
4903   DMLabel  label;
4904   PetscInt ct;
4905 
4906   PetscFunctionBegin;
4907   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4908   PetscValidPointer(celltype, 3);
4909   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4910   PetscCall(DMLabelGetValue(label, cell, &ct));
4911   PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4912   *celltype = (DMPolytopeType)ct;
4913   PetscFunctionReturn(0);
4914 }
4915 
4916 /*@
4917   DMPlexSetCellType - Set the polytope type of a given cell
4918 
4919   Not Collective
4920 
4921   Input Parameters:
4922 + dm   - The DMPlex object
4923 . cell - The cell
4924 - celltype - The polytope type of the cell
4925 
4926   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
4927   is executed. This function will override the computed type. However, if automatic classification will not succeed
4928   and a user wants to manually specify all types, the classification must be disabled by calling
4929   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
4930 
4931   Level: advanced
4932 
4933 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
4934 @*/
4935 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype) {
4936   DMLabel label;
4937 
4938   PetscFunctionBegin;
4939   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4940   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4941   PetscCall(DMLabelSetValue(label, cell, celltype));
4942   PetscFunctionReturn(0);
4943 }
4944 
4945 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm) {
4946   PetscSection section, s;
4947   Mat          m;
4948   PetscInt     maxHeight;
4949 
4950   PetscFunctionBegin;
4951   PetscCall(DMClone(dm, cdm));
4952   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
4953   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
4954   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
4955   PetscCall(DMSetLocalSection(*cdm, section));
4956   PetscCall(PetscSectionDestroy(&section));
4957   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
4958   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
4959   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
4960   PetscCall(PetscSectionDestroy(&s));
4961   PetscCall(MatDestroy(&m));
4962 
4963   PetscCall(DMSetNumFields(*cdm, 1));
4964   PetscCall(DMCreateDS(*cdm));
4965   PetscFunctionReturn(0);
4966 }
4967 
4968 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field) {
4969   Vec coordsLocal, cellCoordsLocal;
4970   DM  coordsDM, cellCoordsDM;
4971 
4972   PetscFunctionBegin;
4973   *field = NULL;
4974   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
4975   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
4976   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
4977   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
4978   if (coordsLocal && coordsDM) {
4979     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
4980     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
4981   }
4982   PetscFunctionReturn(0);
4983 }
4984 
4985 /*@C
4986   DMPlexGetConeSection - Return a section which describes the layout of cone data
4987 
4988   Not Collective
4989 
4990   Input Parameters:
4991 . dm        - The DMPlex object
4992 
4993   Output Parameter:
4994 . section - The PetscSection object
4995 
4996   Level: developer
4997 
4998 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
4999 @*/
5000 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section) {
5001   DM_Plex *mesh = (DM_Plex *)dm->data;
5002 
5003   PetscFunctionBegin;
5004   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5005   if (section) *section = mesh->coneSection;
5006   PetscFunctionReturn(0);
5007 }
5008 
5009 /*@C
5010   DMPlexGetSupportSection - Return a section which describes the layout of support data
5011 
5012   Not Collective
5013 
5014   Input Parameters:
5015 . dm        - The DMPlex object
5016 
5017   Output Parameter:
5018 . section - The PetscSection object
5019 
5020   Level: developer
5021 
5022 .seealso: `DMPlexGetConeSection()`
5023 @*/
5024 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section) {
5025   DM_Plex *mesh = (DM_Plex *)dm->data;
5026 
5027   PetscFunctionBegin;
5028   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5029   if (section) *section = mesh->supportSection;
5030   PetscFunctionReturn(0);
5031 }
5032 
5033 /*@C
5034   DMPlexGetCones - Return cone data
5035 
5036   Not Collective
5037 
5038   Input Parameters:
5039 . dm        - The DMPlex object
5040 
5041   Output Parameter:
5042 . cones - The cone for each point
5043 
5044   Level: developer
5045 
5046 .seealso: `DMPlexGetConeSection()`
5047 @*/
5048 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[]) {
5049   DM_Plex *mesh = (DM_Plex *)dm->data;
5050 
5051   PetscFunctionBegin;
5052   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5053   if (cones) *cones = mesh->cones;
5054   PetscFunctionReturn(0);
5055 }
5056 
5057 /*@C
5058   DMPlexGetConeOrientations - Return cone orientation data
5059 
5060   Not Collective
5061 
5062   Input Parameters:
5063 . dm        - The DMPlex object
5064 
5065   Output Parameter:
5066 . coneOrientations - The array of cone orientations for all points
5067 
5068   Level: developer
5069 
5070   Notes:
5071   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5072 
5073   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5074 
5075 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5076 @*/
5077 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[]) {
5078   DM_Plex *mesh = (DM_Plex *)dm->data;
5079 
5080   PetscFunctionBegin;
5081   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5082   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5083   PetscFunctionReturn(0);
5084 }
5085 
5086 /******************************** FEM Support **********************************/
5087 
5088 /*
5089  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5090  representing a line in the section.
5091 */
5092 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k) {
5093   PetscFunctionBeginHot;
5094   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5095   if (line < 0) {
5096     *k  = 0;
5097     *Nc = 0;
5098   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5099     *k = 1;
5100   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5101     /* An order k SEM disc has k-1 dofs on an edge */
5102     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5103     *k = *k / *Nc + 1;
5104   }
5105   PetscFunctionReturn(0);
5106 }
5107 
5108 /*@
5109 
5110   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5111   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5112   section provided (or the section of the DM).
5113 
5114   Input Parameters:
5115 + dm      - The DM
5116 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5117 - section - The PetscSection to reorder, or NULL for the default section
5118 
5119   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5120   degree of the basis.
5121 
5122   Example:
5123   A typical interpolated single-quad mesh might order points as
5124 .vb
5125   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5126 
5127   v4 -- e6 -- v3
5128   |           |
5129   e7    c0    e8
5130   |           |
5131   v1 -- e5 -- v2
5132 .ve
5133 
5134   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5135   dofs in the order of points, e.g.,
5136 .vb
5137     c0 -> [0,1,2,3]
5138     v1 -> [4]
5139     ...
5140     e5 -> [8, 9]
5141 .ve
5142 
5143   which corresponds to the dofs
5144 .vb
5145     6   10  11  7
5146     13  2   3   15
5147     12  0   1   14
5148     4   8   9   5
5149 .ve
5150 
5151   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5152 .vb
5153   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5154 .ve
5155 
5156   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5157 .vb
5158    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5159 .ve
5160 
5161   Level: developer
5162 
5163 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5164 @*/
5165 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section) {
5166   DMLabel   label;
5167   PetscInt  dim, depth = -1, eStart = -1, Nf;
5168   PetscBool vertexchart;
5169 
5170   PetscFunctionBegin;
5171   PetscCall(DMGetDimension(dm, &dim));
5172   if (dim < 1) PetscFunctionReturn(0);
5173   if (point < 0) {
5174     PetscInt sStart, sEnd;
5175 
5176     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5177     point = sEnd - sStart ? sStart : point;
5178   }
5179   PetscCall(DMPlexGetDepthLabel(dm, &label));
5180   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5181   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5182   if (depth == 1) {
5183     eStart = point;
5184   } else if (depth == dim) {
5185     const PetscInt *cone;
5186 
5187     PetscCall(DMPlexGetCone(dm, point, &cone));
5188     if (dim == 2) eStart = cone[0];
5189     else if (dim == 3) {
5190       const PetscInt *cone2;
5191       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5192       eStart = cone2[0];
5193     } 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);
5194   } 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);
5195   { /* Determine whether the chart covers all points or just vertices. */
5196     PetscInt pStart, pEnd, cStart, cEnd;
5197     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5198     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5199     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5200     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5201     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5202   }
5203   PetscCall(PetscSectionGetNumFields(section, &Nf));
5204   for (PetscInt d = 1; d <= dim; d++) {
5205     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5206     PetscInt *perm;
5207 
5208     for (f = 0; f < Nf; ++f) {
5209       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5210       size += PetscPowInt(k + 1, d) * Nc;
5211     }
5212     PetscCall(PetscMalloc1(size, &perm));
5213     for (f = 0; f < Nf; ++f) {
5214       switch (d) {
5215       case 1:
5216         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5217         /*
5218          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5219          We want              [ vtx0; edge of length k-1; vtx1 ]
5220          */
5221         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5222         for (i = 0; i < k - 1; i++)
5223           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5224         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5225         foffset = offset;
5226         break;
5227       case 2:
5228         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5229         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5230         /* The SEM order is
5231 
5232          v_lb, {e_b}, v_rb,
5233          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5234          v_lt, reverse {e_t}, v_rt
5235          */
5236         {
5237           const PetscInt of   = 0;
5238           const PetscInt oeb  = of + PetscSqr(k - 1);
5239           const PetscInt oer  = oeb + (k - 1);
5240           const PetscInt oet  = oer + (k - 1);
5241           const PetscInt oel  = oet + (k - 1);
5242           const PetscInt ovlb = oel + (k - 1);
5243           const PetscInt ovrb = ovlb + 1;
5244           const PetscInt ovrt = ovrb + 1;
5245           const PetscInt ovlt = ovrt + 1;
5246           PetscInt       o;
5247 
5248           /* bottom */
5249           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5250           for (o = oeb; o < oer; ++o)
5251             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5252           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5253           /* middle */
5254           for (i = 0; i < k - 1; ++i) {
5255             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5256             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5257               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5258             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5259           }
5260           /* top */
5261           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5262           for (o = oel - 1; o >= oet; --o)
5263             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5264           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5265           foffset = offset;
5266         }
5267         break;
5268       case 3:
5269         /* The original hex closure is
5270 
5271          {c,
5272          f_b, f_t, f_f, f_b, f_r, f_l,
5273          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5274          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5275          */
5276         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5277         /* The SEM order is
5278          Bottom Slice
5279          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5280          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5281          v_blb, {e_bb}, v_brb,
5282 
5283          Middle Slice (j)
5284          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5285          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5286          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5287 
5288          Top Slice
5289          v_tlf, {e_tf}, v_trf,
5290          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5291          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5292          */
5293         {
5294           const PetscInt oc    = 0;
5295           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5296           const PetscInt oft   = ofb + PetscSqr(k - 1);
5297           const PetscInt off   = oft + PetscSqr(k - 1);
5298           const PetscInt ofk   = off + PetscSqr(k - 1);
5299           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5300           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5301           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5302           const PetscInt oebb  = oebl + (k - 1);
5303           const PetscInt oebr  = oebb + (k - 1);
5304           const PetscInt oebf  = oebr + (k - 1);
5305           const PetscInt oetf  = oebf + (k - 1);
5306           const PetscInt oetr  = oetf + (k - 1);
5307           const PetscInt oetb  = oetr + (k - 1);
5308           const PetscInt oetl  = oetb + (k - 1);
5309           const PetscInt oerf  = oetl + (k - 1);
5310           const PetscInt oelf  = oerf + (k - 1);
5311           const PetscInt oelb  = oelf + (k - 1);
5312           const PetscInt oerb  = oelb + (k - 1);
5313           const PetscInt ovblf = oerb + (k - 1);
5314           const PetscInt ovblb = ovblf + 1;
5315           const PetscInt ovbrb = ovblb + 1;
5316           const PetscInt ovbrf = ovbrb + 1;
5317           const PetscInt ovtlf = ovbrf + 1;
5318           const PetscInt ovtrf = ovtlf + 1;
5319           const PetscInt ovtrb = ovtrf + 1;
5320           const PetscInt ovtlb = ovtrb + 1;
5321           PetscInt       o, n;
5322 
5323           /* Bottom Slice */
5324           /*   bottom */
5325           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5326           for (o = oetf - 1; o >= oebf; --o)
5327             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5328           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5329           /*   middle */
5330           for (i = 0; i < k - 1; ++i) {
5331             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5332             for (n = 0; n < k - 1; ++n) {
5333               o = ofb + n * (k - 1) + i;
5334               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5335             }
5336             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5337           }
5338           /*   top */
5339           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5340           for (o = oebb; o < oebr; ++o)
5341             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5342           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5343 
5344           /* Middle Slice */
5345           for (j = 0; j < k - 1; ++j) {
5346             /*   bottom */
5347             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5348             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5349               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5350             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5351             /*   middle */
5352             for (i = 0; i < k - 1; ++i) {
5353               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5354               for (n = 0; n < k - 1; ++n)
5355                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5356               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5357             }
5358             /*   top */
5359             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5360             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5361               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5362             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5363           }
5364 
5365           /* Top Slice */
5366           /*   bottom */
5367           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5368           for (o = oetf; o < oetr; ++o)
5369             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5370           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5371           /*   middle */
5372           for (i = 0; i < k - 1; ++i) {
5373             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5374             for (n = 0; n < k - 1; ++n)
5375               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5376             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5377           }
5378           /*   top */
5379           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5380           for (o = oetl - 1; o >= oetb; --o)
5381             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5382           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5383 
5384           foffset = offset;
5385         }
5386         break;
5387       default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5388       }
5389     }
5390     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5391     /* Check permutation */
5392     {
5393       PetscInt *check;
5394 
5395       PetscCall(PetscMalloc1(size, &check));
5396       for (i = 0; i < size; ++i) {
5397         check[i] = -1;
5398         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5399       }
5400       for (i = 0; i < size; ++i) check[perm[i]] = i;
5401       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5402       PetscCall(PetscFree(check));
5403     }
5404     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5405     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5406       PetscInt *loc_perm;
5407       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5408       for (PetscInt i = 0; i < size; i++) {
5409         loc_perm[i]        = perm[i];
5410         loc_perm[size + i] = size + perm[i];
5411       }
5412       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5413     }
5414   }
5415   PetscFunctionReturn(0);
5416 }
5417 
5418 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace) {
5419   PetscDS  prob;
5420   PetscInt depth, Nf, h;
5421   DMLabel  label;
5422 
5423   PetscFunctionBeginHot;
5424   PetscCall(DMGetDS(dm, &prob));
5425   Nf      = prob->Nf;
5426   label   = dm->depthLabel;
5427   *dspace = NULL;
5428   if (field < Nf) {
5429     PetscObject disc = prob->disc[field];
5430 
5431     if (disc->classid == PETSCFE_CLASSID) {
5432       PetscDualSpace dsp;
5433 
5434       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5435       PetscCall(DMLabelGetNumValues(label, &depth));
5436       PetscCall(DMLabelGetValue(label, point, &h));
5437       h = depth - 1 - h;
5438       if (h) {
5439         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5440       } else {
5441         *dspace = dsp;
5442       }
5443     }
5444   }
5445   PetscFunctionReturn(0);
5446 }
5447 
5448 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[]) {
5449   PetscScalar       *array;
5450   const PetscScalar *vArray;
5451   const PetscInt    *cone, *coneO;
5452   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5453 
5454   PetscFunctionBeginHot;
5455   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5456   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5457   PetscCall(DMPlexGetCone(dm, point, &cone));
5458   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5459   if (!values || !*values) {
5460     if ((point >= pStart) && (point < pEnd)) {
5461       PetscInt dof;
5462 
5463       PetscCall(PetscSectionGetDof(section, point, &dof));
5464       size += dof;
5465     }
5466     for (p = 0; p < numPoints; ++p) {
5467       const PetscInt cp = cone[p];
5468       PetscInt       dof;
5469 
5470       if ((cp < pStart) || (cp >= pEnd)) continue;
5471       PetscCall(PetscSectionGetDof(section, cp, &dof));
5472       size += dof;
5473     }
5474     if (!values) {
5475       if (csize) *csize = size;
5476       PetscFunctionReturn(0);
5477     }
5478     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5479   } else {
5480     array = *values;
5481   }
5482   size = 0;
5483   PetscCall(VecGetArrayRead(v, &vArray));
5484   if ((point >= pStart) && (point < pEnd)) {
5485     PetscInt           dof, off, d;
5486     const PetscScalar *varr;
5487 
5488     PetscCall(PetscSectionGetDof(section, point, &dof));
5489     PetscCall(PetscSectionGetOffset(section, point, &off));
5490     varr = &vArray[off];
5491     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5492     size += dof;
5493   }
5494   for (p = 0; p < numPoints; ++p) {
5495     const PetscInt     cp = cone[p];
5496     PetscInt           o  = coneO[p];
5497     PetscInt           dof, off, d;
5498     const PetscScalar *varr;
5499 
5500     if ((cp < pStart) || (cp >= pEnd)) continue;
5501     PetscCall(PetscSectionGetDof(section, cp, &dof));
5502     PetscCall(PetscSectionGetOffset(section, cp, &off));
5503     varr = &vArray[off];
5504     if (o >= 0) {
5505       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5506     } else {
5507       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5508     }
5509     size += dof;
5510   }
5511   PetscCall(VecRestoreArrayRead(v, &vArray));
5512   if (!*values) {
5513     if (csize) *csize = size;
5514     *values = array;
5515   } else {
5516     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5517     *csize = size;
5518   }
5519   PetscFunctionReturn(0);
5520 }
5521 
5522 /* Compress out points not in the section */
5523 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[]) {
5524   const PetscInt np = *numPoints;
5525   PetscInt       pStart, pEnd, p, q;
5526 
5527   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5528   for (p = 0, q = 0; p < np; ++p) {
5529     const PetscInt r = points[p * 2];
5530     if ((r >= pStart) && (r < pEnd)) {
5531       points[q * 2]     = r;
5532       points[q * 2 + 1] = points[p * 2 + 1];
5533       ++q;
5534     }
5535   }
5536   *numPoints = q;
5537   return 0;
5538 }
5539 
5540 /* Compressed closure does not apply closure permutation */
5541 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp) {
5542   const PetscInt *cla = NULL;
5543   PetscInt        np, *pts = NULL;
5544 
5545   PetscFunctionBeginHot;
5546   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5547   if (*clPoints) {
5548     PetscInt dof, off;
5549 
5550     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5551     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5552     PetscCall(ISGetIndices(*clPoints, &cla));
5553     np  = dof / 2;
5554     pts = (PetscInt *)&cla[off];
5555   } else {
5556     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5557     PetscCall(CompressPoints_Private(section, &np, pts));
5558   }
5559   *numPoints = np;
5560   *points    = pts;
5561   *clp       = cla;
5562   PetscFunctionReturn(0);
5563 }
5564 
5565 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp) {
5566   PetscFunctionBeginHot;
5567   if (!*clPoints) {
5568     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5569   } else {
5570     PetscCall(ISRestoreIndices(*clPoints, clp));
5571   }
5572   *numPoints = 0;
5573   *points    = NULL;
5574   *clSec     = NULL;
5575   *clPoints  = NULL;
5576   *clp       = NULL;
5577   PetscFunctionReturn(0);
5578 }
5579 
5580 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[]) {
5581   PetscInt            offset = 0, p;
5582   const PetscInt    **perms  = NULL;
5583   const PetscScalar **flips  = NULL;
5584 
5585   PetscFunctionBeginHot;
5586   *size = 0;
5587   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5588   for (p = 0; p < numPoints; p++) {
5589     const PetscInt     point = points[2 * p];
5590     const PetscInt    *perm  = perms ? perms[p] : NULL;
5591     const PetscScalar *flip  = flips ? flips[p] : NULL;
5592     PetscInt           dof, off, d;
5593     const PetscScalar *varr;
5594 
5595     PetscCall(PetscSectionGetDof(section, point, &dof));
5596     PetscCall(PetscSectionGetOffset(section, point, &off));
5597     varr = &vArray[off];
5598     if (clperm) {
5599       if (perm) {
5600         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5601       } else {
5602         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5603       }
5604       if (flip) {
5605         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5606       }
5607     } else {
5608       if (perm) {
5609         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5610       } else {
5611         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5612       }
5613       if (flip) {
5614         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5615       }
5616     }
5617     offset += dof;
5618   }
5619   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5620   *size = offset;
5621   PetscFunctionReturn(0);
5622 }
5623 
5624 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[]) {
5625   PetscInt offset = 0, f;
5626 
5627   PetscFunctionBeginHot;
5628   *size = 0;
5629   for (f = 0; f < numFields; ++f) {
5630     PetscInt            p;
5631     const PetscInt    **perms = NULL;
5632     const PetscScalar **flips = NULL;
5633 
5634     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5635     for (p = 0; p < numPoints; p++) {
5636       const PetscInt     point = points[2 * p];
5637       PetscInt           fdof, foff, b;
5638       const PetscScalar *varr;
5639       const PetscInt    *perm = perms ? perms[p] : NULL;
5640       const PetscScalar *flip = flips ? flips[p] : NULL;
5641 
5642       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5643       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5644       varr = &vArray[foff];
5645       if (clperm) {
5646         if (perm) {
5647           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5648         } else {
5649           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5650         }
5651         if (flip) {
5652           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5653         }
5654       } else {
5655         if (perm) {
5656           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5657         } else {
5658           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5659         }
5660         if (flip) {
5661           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5662         }
5663       }
5664       offset += fdof;
5665     }
5666     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5667   }
5668   *size = offset;
5669   PetscFunctionReturn(0);
5670 }
5671 
5672 /*@C
5673   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5674 
5675   Not collective
5676 
5677   Input Parameters:
5678 + dm - The DM
5679 . section - The section describing the layout in v, or NULL to use the default section
5680 . v - The local vector
5681 - point - The point in the DM
5682 
5683   Input/Output Parameters:
5684 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5685 - values - An array to use for the values, or NULL to have it allocated automatically;
5686            if the user provided NULL, it is a borrowed array and should not be freed
5687 
5688 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5689 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5690 $ assembly function, and a user may already have allocated storage for this operation.
5691 $
5692 $ A typical use could be
5693 $
5694 $  values = NULL;
5695 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5696 $  for (cl = 0; cl < clSize; ++cl) {
5697 $    <Compute on closure>
5698 $  }
5699 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5700 $
5701 $ or
5702 $
5703 $  PetscMalloc1(clMaxSize, &values);
5704 $  for (p = pStart; p < pEnd; ++p) {
5705 $    clSize = clMaxSize;
5706 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5707 $    for (cl = 0; cl < clSize; ++cl) {
5708 $      <Compute on closure>
5709 $    }
5710 $  }
5711 $  PetscFree(values);
5712 
5713   Fortran Notes:
5714   Since it returns an array, this routine is only available in Fortran 90, and you must
5715   include petsc.h90 in your code.
5716 
5717   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5718 
5719   Level: intermediate
5720 
5721 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5722 @*/
5723 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[]) {
5724   PetscSection    clSection;
5725   IS              clPoints;
5726   PetscInt       *points = NULL;
5727   const PetscInt *clp, *perm;
5728   PetscInt        depth, numFields, numPoints, asize;
5729 
5730   PetscFunctionBeginHot;
5731   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5732   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5733   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5734   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5735   PetscCall(DMPlexGetDepth(dm, &depth));
5736   PetscCall(PetscSectionGetNumFields(section, &numFields));
5737   if (depth == 1 && numFields < 2) {
5738     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5739     PetscFunctionReturn(0);
5740   }
5741   /* Get points */
5742   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5743   /* Get sizes */
5744   asize = 0;
5745   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
5746     PetscInt dof;
5747     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5748     asize += dof;
5749   }
5750   if (values) {
5751     const PetscScalar *vArray;
5752     PetscInt           size;
5753 
5754     if (*values) {
5755       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);
5756     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5757     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
5758     PetscCall(VecGetArrayRead(v, &vArray));
5759     /* Get values */
5760     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5761     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5762     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5763     /* Cleanup array */
5764     PetscCall(VecRestoreArrayRead(v, &vArray));
5765   }
5766   if (csize) *csize = asize;
5767   /* Cleanup points */
5768   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5769   PetscFunctionReturn(0);
5770 }
5771 
5772 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[]) {
5773   DMLabel            depthLabel;
5774   PetscSection       clSection;
5775   IS                 clPoints;
5776   PetscScalar       *array;
5777   const PetscScalar *vArray;
5778   PetscInt          *points = NULL;
5779   const PetscInt    *clp, *perm = NULL;
5780   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5781 
5782   PetscFunctionBeginHot;
5783   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5784   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5785   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5786   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5787   PetscCall(DMPlexGetDepth(dm, &mdepth));
5788   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5789   PetscCall(PetscSectionGetNumFields(section, &numFields));
5790   if (mdepth == 1 && numFields < 2) {
5791     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5792     PetscFunctionReturn(0);
5793   }
5794   /* Get points */
5795   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5796   for (clsize = 0, p = 0; p < Np; p++) {
5797     PetscInt dof;
5798     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
5799     clsize += dof;
5800   }
5801   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
5802   /* Filter points */
5803   for (p = 0; p < numPoints * 2; p += 2) {
5804     PetscInt dep;
5805 
5806     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5807     if (dep != depth) continue;
5808     points[Np * 2 + 0] = points[p];
5809     points[Np * 2 + 1] = points[p + 1];
5810     ++Np;
5811   }
5812   /* Get array */
5813   if (!values || !*values) {
5814     PetscInt asize = 0, dof;
5815 
5816     for (p = 0; p < Np * 2; p += 2) {
5817       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5818       asize += dof;
5819     }
5820     if (!values) {
5821       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5822       if (csize) *csize = asize;
5823       PetscFunctionReturn(0);
5824     }
5825     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5826   } else {
5827     array = *values;
5828   }
5829   PetscCall(VecGetArrayRead(v, &vArray));
5830   /* Get values */
5831   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5832   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5833   /* Cleanup points */
5834   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5835   /* Cleanup array */
5836   PetscCall(VecRestoreArrayRead(v, &vArray));
5837   if (!*values) {
5838     if (csize) *csize = size;
5839     *values = array;
5840   } else {
5841     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5842     *csize = size;
5843   }
5844   PetscFunctionReturn(0);
5845 }
5846 
5847 /*@C
5848   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5849 
5850   Not collective
5851 
5852   Input Parameters:
5853 + dm - The DM
5854 . section - The section describing the layout in v, or NULL to use the default section
5855 . v - The local vector
5856 . point - The point in the DM
5857 . csize - The number of values in the closure, or NULL
5858 - values - The array of values, which is a borrowed array and should not be freed
5859 
5860   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5861 
5862   Fortran Notes:
5863   Since it returns an array, this routine is only available in Fortran 90, and you must
5864   include petsc.h90 in your code.
5865 
5866   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5867 
5868   Level: intermediate
5869 
5870 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5871 @*/
5872 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[]) {
5873   PetscInt size = 0;
5874 
5875   PetscFunctionBegin;
5876   /* Should work without recalculating size */
5877   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
5878   *values = NULL;
5879   PetscFunctionReturn(0);
5880 }
5881 
5882 static inline void add(PetscScalar *x, PetscScalar y) {
5883   *x += y;
5884 }
5885 static inline void insert(PetscScalar *x, PetscScalar y) {
5886   *x = y;
5887 }
5888 
5889 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[]) {
5890   PetscInt        cdof;  /* The number of constraints on this point */
5891   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5892   PetscScalar    *a;
5893   PetscInt        off, cind = 0, k;
5894 
5895   PetscFunctionBegin;
5896   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5897   PetscCall(PetscSectionGetOffset(section, point, &off));
5898   a = &array[off];
5899   if (!cdof || setBC) {
5900     if (clperm) {
5901       if (perm) {
5902         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
5903       } else {
5904         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
5905       }
5906     } else {
5907       if (perm) {
5908         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
5909       } else {
5910         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
5911       }
5912     }
5913   } else {
5914     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5915     if (clperm) {
5916       if (perm) {
5917         for (k = 0; k < dof; ++k) {
5918           if ((cind < cdof) && (k == cdofs[cind])) {
5919             ++cind;
5920             continue;
5921           }
5922           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
5923         }
5924       } else {
5925         for (k = 0; k < dof; ++k) {
5926           if ((cind < cdof) && (k == cdofs[cind])) {
5927             ++cind;
5928             continue;
5929           }
5930           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
5931         }
5932       }
5933     } else {
5934       if (perm) {
5935         for (k = 0; k < dof; ++k) {
5936           if ((cind < cdof) && (k == cdofs[cind])) {
5937             ++cind;
5938             continue;
5939           }
5940           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
5941         }
5942       } else {
5943         for (k = 0; k < dof; ++k) {
5944           if ((cind < cdof) && (k == cdofs[cind])) {
5945             ++cind;
5946             continue;
5947           }
5948           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
5949         }
5950       }
5951     }
5952   }
5953   PetscFunctionReturn(0);
5954 }
5955 
5956 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[]) {
5957   PetscInt        cdof;  /* The number of constraints on this point */
5958   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5959   PetscScalar    *a;
5960   PetscInt        off, cind = 0, k;
5961 
5962   PetscFunctionBegin;
5963   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5964   PetscCall(PetscSectionGetOffset(section, point, &off));
5965   a = &array[off];
5966   if (cdof) {
5967     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5968     if (clperm) {
5969       if (perm) {
5970         for (k = 0; k < dof; ++k) {
5971           if ((cind < cdof) && (k == cdofs[cind])) {
5972             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
5973             cind++;
5974           }
5975         }
5976       } else {
5977         for (k = 0; k < dof; ++k) {
5978           if ((cind < cdof) && (k == cdofs[cind])) {
5979             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
5980             cind++;
5981           }
5982         }
5983       }
5984     } else {
5985       if (perm) {
5986         for (k = 0; k < dof; ++k) {
5987           if ((cind < cdof) && (k == cdofs[cind])) {
5988             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
5989             cind++;
5990           }
5991         }
5992       } else {
5993         for (k = 0; k < dof; ++k) {
5994           if ((cind < cdof) && (k == cdofs[cind])) {
5995             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
5996             cind++;
5997           }
5998         }
5999       }
6000     }
6001   }
6002   PetscFunctionReturn(0);
6003 }
6004 
6005 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[]) {
6006   PetscScalar    *a;
6007   PetscInt        fdof, foff, fcdof, foffset = *offset;
6008   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6009   PetscInt        cind = 0, b;
6010 
6011   PetscFunctionBegin;
6012   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6013   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6014   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6015   a = &array[foff];
6016   if (!fcdof || setBC) {
6017     if (clperm) {
6018       if (perm) {
6019         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6020       } else {
6021         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6022       }
6023     } else {
6024       if (perm) {
6025         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6026       } else {
6027         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6028       }
6029     }
6030   } else {
6031     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6032     if (clperm) {
6033       if (perm) {
6034         for (b = 0; b < fdof; b++) {
6035           if ((cind < fcdof) && (b == fcdofs[cind])) {
6036             ++cind;
6037             continue;
6038           }
6039           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6040         }
6041       } else {
6042         for (b = 0; b < fdof; b++) {
6043           if ((cind < fcdof) && (b == fcdofs[cind])) {
6044             ++cind;
6045             continue;
6046           }
6047           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6048         }
6049       }
6050     } else {
6051       if (perm) {
6052         for (b = 0; b < fdof; b++) {
6053           if ((cind < fcdof) && (b == fcdofs[cind])) {
6054             ++cind;
6055             continue;
6056           }
6057           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6058         }
6059       } else {
6060         for (b = 0; b < fdof; b++) {
6061           if ((cind < fcdof) && (b == fcdofs[cind])) {
6062             ++cind;
6063             continue;
6064           }
6065           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6066         }
6067       }
6068     }
6069   }
6070   *offset += fdof;
6071   PetscFunctionReturn(0);
6072 }
6073 
6074 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[]) {
6075   PetscScalar    *a;
6076   PetscInt        fdof, foff, fcdof, foffset = *offset;
6077   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6078   PetscInt        Nc, cind = 0, ncind = 0, b;
6079   PetscBool       ncSet, fcSet;
6080 
6081   PetscFunctionBegin;
6082   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6083   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6084   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6085   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6086   a = &array[foff];
6087   if (fcdof) {
6088     /* We just override fcdof and fcdofs with Ncc and comps */
6089     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6090     if (clperm) {
6091       if (perm) {
6092         if (comps) {
6093           for (b = 0; b < fdof; b++) {
6094             ncSet = fcSet = PETSC_FALSE;
6095             if (b % Nc == comps[ncind]) {
6096               ncind = (ncind + 1) % Ncc;
6097               ncSet = PETSC_TRUE;
6098             }
6099             if ((cind < fcdof) && (b == fcdofs[cind])) {
6100               ++cind;
6101               fcSet = PETSC_TRUE;
6102             }
6103             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6104           }
6105         } else {
6106           for (b = 0; b < fdof; b++) {
6107             if ((cind < fcdof) && (b == fcdofs[cind])) {
6108               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6109               ++cind;
6110             }
6111           }
6112         }
6113       } else {
6114         if (comps) {
6115           for (b = 0; b < fdof; b++) {
6116             ncSet = fcSet = PETSC_FALSE;
6117             if (b % Nc == comps[ncind]) {
6118               ncind = (ncind + 1) % Ncc;
6119               ncSet = PETSC_TRUE;
6120             }
6121             if ((cind < fcdof) && (b == fcdofs[cind])) {
6122               ++cind;
6123               fcSet = PETSC_TRUE;
6124             }
6125             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6126           }
6127         } else {
6128           for (b = 0; b < fdof; b++) {
6129             if ((cind < fcdof) && (b == fcdofs[cind])) {
6130               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6131               ++cind;
6132             }
6133           }
6134         }
6135       }
6136     } else {
6137       if (perm) {
6138         if (comps) {
6139           for (b = 0; b < fdof; b++) {
6140             ncSet = fcSet = PETSC_FALSE;
6141             if (b % Nc == comps[ncind]) {
6142               ncind = (ncind + 1) % Ncc;
6143               ncSet = PETSC_TRUE;
6144             }
6145             if ((cind < fcdof) && (b == fcdofs[cind])) {
6146               ++cind;
6147               fcSet = PETSC_TRUE;
6148             }
6149             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6150           }
6151         } else {
6152           for (b = 0; b < fdof; b++) {
6153             if ((cind < fcdof) && (b == fcdofs[cind])) {
6154               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6155               ++cind;
6156             }
6157           }
6158         }
6159       } else {
6160         if (comps) {
6161           for (b = 0; b < fdof; b++) {
6162             ncSet = fcSet = PETSC_FALSE;
6163             if (b % Nc == comps[ncind]) {
6164               ncind = (ncind + 1) % Ncc;
6165               ncSet = PETSC_TRUE;
6166             }
6167             if ((cind < fcdof) && (b == fcdofs[cind])) {
6168               ++cind;
6169               fcSet = PETSC_TRUE;
6170             }
6171             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6172           }
6173         } else {
6174           for (b = 0; b < fdof; b++) {
6175             if ((cind < fcdof) && (b == fcdofs[cind])) {
6176               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6177               ++cind;
6178             }
6179           }
6180         }
6181       }
6182     }
6183   }
6184   *offset += fdof;
6185   PetscFunctionReturn(0);
6186 }
6187 
6188 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode) {
6189   PetscScalar    *array;
6190   const PetscInt *cone, *coneO;
6191   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6192 
6193   PetscFunctionBeginHot;
6194   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6195   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6196   PetscCall(DMPlexGetCone(dm, point, &cone));
6197   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6198   PetscCall(VecGetArray(v, &array));
6199   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6200     const PetscInt cp = !p ? point : cone[p - 1];
6201     const PetscInt o  = !p ? 0 : coneO[p - 1];
6202 
6203     if ((cp < pStart) || (cp >= pEnd)) {
6204       dof = 0;
6205       continue;
6206     }
6207     PetscCall(PetscSectionGetDof(section, cp, &dof));
6208     /* ADD_VALUES */
6209     {
6210       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6211       PetscScalar    *a;
6212       PetscInt        cdof, coff, cind = 0, k;
6213 
6214       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6215       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6216       a = &array[coff];
6217       if (!cdof) {
6218         if (o >= 0) {
6219           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6220         } else {
6221           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6222         }
6223       } else {
6224         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6225         if (o >= 0) {
6226           for (k = 0; k < dof; ++k) {
6227             if ((cind < cdof) && (k == cdofs[cind])) {
6228               ++cind;
6229               continue;
6230             }
6231             a[k] += values[off + k];
6232           }
6233         } else {
6234           for (k = 0; k < dof; ++k) {
6235             if ((cind < cdof) && (k == cdofs[cind])) {
6236               ++cind;
6237               continue;
6238             }
6239             a[k] += values[off + dof - k - 1];
6240           }
6241         }
6242       }
6243     }
6244   }
6245   PetscCall(VecRestoreArray(v, &array));
6246   PetscFunctionReturn(0);
6247 }
6248 
6249 /*@C
6250   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6251 
6252   Not collective
6253 
6254   Input Parameters:
6255 + dm - The DM
6256 . section - The section describing the layout in v, or NULL to use the default section
6257 . v - The local vector
6258 . point - The point in the DM
6259 . values - The array of values
6260 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6261          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6262 
6263   Fortran Notes:
6264   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6265 
6266   Level: intermediate
6267 
6268 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6269 @*/
6270 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode) {
6271   PetscSection    clSection;
6272   IS              clPoints;
6273   PetscScalar    *array;
6274   PetscInt       *points = NULL;
6275   const PetscInt *clp, *clperm = NULL;
6276   PetscInt        depth, numFields, numPoints, p, clsize;
6277 
6278   PetscFunctionBeginHot;
6279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6280   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6281   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6282   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6283   PetscCall(DMPlexGetDepth(dm, &depth));
6284   PetscCall(PetscSectionGetNumFields(section, &numFields));
6285   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6286     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6287     PetscFunctionReturn(0);
6288   }
6289   /* Get points */
6290   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6291   for (clsize = 0, p = 0; p < numPoints; p++) {
6292     PetscInt dof;
6293     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6294     clsize += dof;
6295   }
6296   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6297   /* Get array */
6298   PetscCall(VecGetArray(v, &array));
6299   /* Get values */
6300   if (numFields > 0) {
6301     PetscInt offset = 0, f;
6302     for (f = 0; f < numFields; ++f) {
6303       const PetscInt    **perms = NULL;
6304       const PetscScalar **flips = NULL;
6305 
6306       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6307       switch (mode) {
6308       case INSERT_VALUES:
6309         for (p = 0; p < numPoints; p++) {
6310           const PetscInt     point = points[2 * p];
6311           const PetscInt    *perm  = perms ? perms[p] : NULL;
6312           const PetscScalar *flip  = flips ? flips[p] : NULL;
6313           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6314         }
6315         break;
6316       case INSERT_ALL_VALUES:
6317         for (p = 0; p < numPoints; p++) {
6318           const PetscInt     point = points[2 * p];
6319           const PetscInt    *perm  = perms ? perms[p] : NULL;
6320           const PetscScalar *flip  = flips ? flips[p] : NULL;
6321           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6322         }
6323         break;
6324       case INSERT_BC_VALUES:
6325         for (p = 0; p < numPoints; p++) {
6326           const PetscInt     point = points[2 * p];
6327           const PetscInt    *perm  = perms ? perms[p] : NULL;
6328           const PetscScalar *flip  = flips ? flips[p] : NULL;
6329           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6330         }
6331         break;
6332       case ADD_VALUES:
6333         for (p = 0; p < numPoints; p++) {
6334           const PetscInt     point = points[2 * p];
6335           const PetscInt    *perm  = perms ? perms[p] : NULL;
6336           const PetscScalar *flip  = flips ? flips[p] : NULL;
6337           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6338         }
6339         break;
6340       case ADD_ALL_VALUES:
6341         for (p = 0; p < numPoints; p++) {
6342           const PetscInt     point = points[2 * p];
6343           const PetscInt    *perm  = perms ? perms[p] : NULL;
6344           const PetscScalar *flip  = flips ? flips[p] : NULL;
6345           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6346         }
6347         break;
6348       case ADD_BC_VALUES:
6349         for (p = 0; p < numPoints; p++) {
6350           const PetscInt     point = points[2 * p];
6351           const PetscInt    *perm  = perms ? perms[p] : NULL;
6352           const PetscScalar *flip  = flips ? flips[p] : NULL;
6353           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6354         }
6355         break;
6356       default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6357       }
6358       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6359     }
6360   } else {
6361     PetscInt            dof, off;
6362     const PetscInt    **perms = NULL;
6363     const PetscScalar **flips = NULL;
6364 
6365     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6366     switch (mode) {
6367     case INSERT_VALUES:
6368       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6369         const PetscInt     point = points[2 * p];
6370         const PetscInt    *perm  = perms ? perms[p] : NULL;
6371         const PetscScalar *flip  = flips ? flips[p] : NULL;
6372         PetscCall(PetscSectionGetDof(section, point, &dof));
6373         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6374       }
6375       break;
6376     case INSERT_ALL_VALUES:
6377       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6378         const PetscInt     point = points[2 * p];
6379         const PetscInt    *perm  = perms ? perms[p] : NULL;
6380         const PetscScalar *flip  = flips ? flips[p] : NULL;
6381         PetscCall(PetscSectionGetDof(section, point, &dof));
6382         updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array);
6383       }
6384       break;
6385     case INSERT_BC_VALUES:
6386       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6387         const PetscInt     point = points[2 * p];
6388         const PetscInt    *perm  = perms ? perms[p] : NULL;
6389         const PetscScalar *flip  = flips ? flips[p] : NULL;
6390         PetscCall(PetscSectionGetDof(section, point, &dof));
6391         updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array);
6392       }
6393       break;
6394     case ADD_VALUES:
6395       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6396         const PetscInt     point = points[2 * p];
6397         const PetscInt    *perm  = perms ? perms[p] : NULL;
6398         const PetscScalar *flip  = flips ? flips[p] : NULL;
6399         PetscCall(PetscSectionGetDof(section, point, &dof));
6400         updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array);
6401       }
6402       break;
6403     case ADD_ALL_VALUES:
6404       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6405         const PetscInt     point = points[2 * p];
6406         const PetscInt    *perm  = perms ? perms[p] : NULL;
6407         const PetscScalar *flip  = flips ? flips[p] : NULL;
6408         PetscCall(PetscSectionGetDof(section, point, &dof));
6409         updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array);
6410       }
6411       break;
6412     case ADD_BC_VALUES:
6413       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6414         const PetscInt     point = points[2 * p];
6415         const PetscInt    *perm  = perms ? perms[p] : NULL;
6416         const PetscScalar *flip  = flips ? flips[p] : NULL;
6417         PetscCall(PetscSectionGetDof(section, point, &dof));
6418         updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array);
6419       }
6420       break;
6421     default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6422     }
6423     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6424   }
6425   /* Cleanup points */
6426   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6427   /* Cleanup array */
6428   PetscCall(VecRestoreArray(v, &array));
6429   PetscFunctionReturn(0);
6430 }
6431 
6432 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6433 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains) {
6434   PetscFunctionBegin;
6435   *contains = PETSC_TRUE;
6436   if (label) {
6437     PetscInt fdof;
6438 
6439     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6440     if (!*contains) {
6441       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6442       *offset += fdof;
6443       PetscFunctionReturn(0);
6444     }
6445   }
6446   PetscFunctionReturn(0);
6447 }
6448 
6449 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6450 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) {
6451   PetscSection    clSection;
6452   IS              clPoints;
6453   PetscScalar    *array;
6454   PetscInt       *points = NULL;
6455   const PetscInt *clp;
6456   PetscInt        numFields, numPoints, p;
6457   PetscInt        offset = 0, f;
6458 
6459   PetscFunctionBeginHot;
6460   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6461   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6462   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6463   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6464   PetscCall(PetscSectionGetNumFields(section, &numFields));
6465   /* Get points */
6466   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6467   /* Get array */
6468   PetscCall(VecGetArray(v, &array));
6469   /* Get values */
6470   for (f = 0; f < numFields; ++f) {
6471     const PetscInt    **perms = NULL;
6472     const PetscScalar **flips = NULL;
6473     PetscBool           contains;
6474 
6475     if (!fieldActive[f]) {
6476       for (p = 0; p < numPoints * 2; p += 2) {
6477         PetscInt fdof;
6478         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6479         offset += fdof;
6480       }
6481       continue;
6482     }
6483     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6484     switch (mode) {
6485     case INSERT_VALUES:
6486       for (p = 0; p < numPoints; p++) {
6487         const PetscInt     point = points[2 * p];
6488         const PetscInt    *perm  = perms ? perms[p] : NULL;
6489         const PetscScalar *flip  = flips ? flips[p] : NULL;
6490         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6491         if (!contains) continue;
6492         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6493       }
6494       break;
6495     case INSERT_ALL_VALUES:
6496       for (p = 0; p < numPoints; p++) {
6497         const PetscInt     point = points[2 * p];
6498         const PetscInt    *perm  = perms ? perms[p] : NULL;
6499         const PetscScalar *flip  = flips ? flips[p] : NULL;
6500         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6501         if (!contains) continue;
6502         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6503       }
6504       break;
6505     case INSERT_BC_VALUES:
6506       for (p = 0; p < numPoints; p++) {
6507         const PetscInt     point = points[2 * p];
6508         const PetscInt    *perm  = perms ? perms[p] : NULL;
6509         const PetscScalar *flip  = flips ? flips[p] : NULL;
6510         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6511         if (!contains) continue;
6512         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6513       }
6514       break;
6515     case ADD_VALUES:
6516       for (p = 0; p < numPoints; p++) {
6517         const PetscInt     point = points[2 * p];
6518         const PetscInt    *perm  = perms ? perms[p] : NULL;
6519         const PetscScalar *flip  = flips ? flips[p] : NULL;
6520         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6521         if (!contains) continue;
6522         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6523       }
6524       break;
6525     case ADD_ALL_VALUES:
6526       for (p = 0; p < numPoints; p++) {
6527         const PetscInt     point = points[2 * p];
6528         const PetscInt    *perm  = perms ? perms[p] : NULL;
6529         const PetscScalar *flip  = flips ? flips[p] : NULL;
6530         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6531         if (!contains) continue;
6532         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6533       }
6534       break;
6535     default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6536     }
6537     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6538   }
6539   /* Cleanup points */
6540   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6541   /* Cleanup array */
6542   PetscCall(VecRestoreArray(v, &array));
6543   PetscFunctionReturn(0);
6544 }
6545 
6546 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[]) {
6547   PetscMPIInt rank;
6548   PetscInt    i, j;
6549 
6550   PetscFunctionBegin;
6551   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6552   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6553   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6554   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6555   numCIndices = numCIndices ? numCIndices : numRIndices;
6556   if (!values) PetscFunctionReturn(0);
6557   for (i = 0; i < numRIndices; i++) {
6558     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6559     for (j = 0; j < numCIndices; j++) {
6560 #if defined(PETSC_USE_COMPLEX)
6561       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6562 #else
6563       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6564 #endif
6565     }
6566     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6567   }
6568   PetscFunctionReturn(0);
6569 }
6570 
6571 /*
6572   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6573 
6574   Input Parameters:
6575 + section - The section for this data layout
6576 . islocal - Is the section (and thus indices being requested) local or global?
6577 . point   - The point contributing dofs with these indices
6578 . off     - The global offset of this point
6579 . loff    - The local offset of each field
6580 . setBC   - The flag determining whether to include indices of boundary values
6581 . perm    - A permutation of the dofs on this point, or NULL
6582 - indperm - A permutation of the entire indices array, or NULL
6583 
6584   Output Parameter:
6585 . indices - Indices for dofs on this point
6586 
6587   Level: developer
6588 
6589   Note: The indices could be local or global, depending on the value of 'off'.
6590 */
6591 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[]) {
6592   PetscInt        dof;   /* The number of unknowns on this point */
6593   PetscInt        cdof;  /* The number of constraints on this point */
6594   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6595   PetscInt        cind = 0, k;
6596 
6597   PetscFunctionBegin;
6598   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6599   PetscCall(PetscSectionGetDof(section, point, &dof));
6600   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6601   if (!cdof || setBC) {
6602     for (k = 0; k < dof; ++k) {
6603       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6604       const PetscInt ind    = indperm ? indperm[preind] : preind;
6605 
6606       indices[ind] = off + k;
6607     }
6608   } else {
6609     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6610     for (k = 0; k < dof; ++k) {
6611       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6612       const PetscInt ind    = indperm ? indperm[preind] : preind;
6613 
6614       if ((cind < cdof) && (k == cdofs[cind])) {
6615         /* Insert check for returning constrained indices */
6616         indices[ind] = -(off + k + 1);
6617         ++cind;
6618       } else {
6619         indices[ind] = off + k - (islocal ? 0 : cind);
6620       }
6621     }
6622   }
6623   *loff += dof;
6624   PetscFunctionReturn(0);
6625 }
6626 
6627 /*
6628  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6629 
6630  Input Parameters:
6631 + section - a section (global or local)
6632 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6633 . point - point within section
6634 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6635 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6636 . setBC - identify constrained (boundary condition) points via involution.
6637 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6638 . permsoff - offset
6639 - indperm - index permutation
6640 
6641  Output Parameter:
6642 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6643 . indices - array to hold indices (as defined by section) of each dof associated with point
6644 
6645  Notes:
6646  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6647  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6648  in the local vector.
6649 
6650  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6651  significant).  It is invalid to call with a global section and setBC=true.
6652 
6653  Developer Note:
6654  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6655  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6656  offset could be obtained from the section instead of passing it explicitly as we do now.
6657 
6658  Example:
6659  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6660  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6661  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6662  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.
6663 
6664  Level: developer
6665 */
6666 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[]) {
6667   PetscInt numFields, foff, f;
6668 
6669   PetscFunctionBegin;
6670   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6671   PetscCall(PetscSectionGetNumFields(section, &numFields));
6672   for (f = 0, foff = 0; f < numFields; ++f) {
6673     PetscInt        fdof, cfdof;
6674     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6675     PetscInt        cind = 0, b;
6676     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6677 
6678     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6679     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6680     if (!cfdof || setBC) {
6681       for (b = 0; b < fdof; ++b) {
6682         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6683         const PetscInt ind    = indperm ? indperm[preind] : preind;
6684 
6685         indices[ind] = off + foff + b;
6686       }
6687     } else {
6688       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6689       for (b = 0; b < fdof; ++b) {
6690         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6691         const PetscInt ind    = indperm ? indperm[preind] : preind;
6692 
6693         if ((cind < cfdof) && (b == fcdofs[cind])) {
6694           indices[ind] = -(off + foff + b + 1);
6695           ++cind;
6696         } else {
6697           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6698         }
6699       }
6700     }
6701     foff += (setBC || islocal ? fdof : (fdof - cfdof));
6702     foffs[f] += fdof;
6703   }
6704   PetscFunctionReturn(0);
6705 }
6706 
6707 /*
6708   This version believes the globalSection offsets for each field, rather than just the point offset
6709 
6710  . foffs - The offset into 'indices' for each field, since it is segregated by field
6711 
6712  Notes:
6713  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6714  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6715 */
6716 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[]) {
6717   PetscInt numFields, foff, f;
6718 
6719   PetscFunctionBegin;
6720   PetscCall(PetscSectionGetNumFields(section, &numFields));
6721   for (f = 0; f < numFields; ++f) {
6722     PetscInt        fdof, cfdof;
6723     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6724     PetscInt        cind = 0, b;
6725     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6726 
6727     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6728     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6729     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6730     if (!cfdof) {
6731       for (b = 0; b < fdof; ++b) {
6732         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6733         const PetscInt ind    = indperm ? indperm[preind] : preind;
6734 
6735         indices[ind] = foff + b;
6736       }
6737     } else {
6738       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6739       for (b = 0; b < fdof; ++b) {
6740         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6741         const PetscInt ind    = indperm ? indperm[preind] : preind;
6742 
6743         if ((cind < cfdof) && (b == fcdofs[cind])) {
6744           indices[ind] = -(foff + b + 1);
6745           ++cind;
6746         } else {
6747           indices[ind] = foff + b - cind;
6748         }
6749       }
6750     }
6751     foffs[f] += fdof;
6752   }
6753   PetscFunctionReturn(0);
6754 }
6755 
6756 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) {
6757   Mat             cMat;
6758   PetscSection    aSec, cSec;
6759   IS              aIS;
6760   PetscInt        aStart = -1, aEnd = -1;
6761   const PetscInt *anchors;
6762   PetscInt        numFields, f, p, q, newP = 0;
6763   PetscInt        newNumPoints = 0, newNumIndices = 0;
6764   PetscInt       *newPoints, *indices, *newIndices;
6765   PetscInt        maxAnchor, maxDof;
6766   PetscInt        newOffsets[32];
6767   PetscInt       *pointMatOffsets[32];
6768   PetscInt       *newPointOffsets[32];
6769   PetscScalar    *pointMat[32];
6770   PetscScalar    *newValues      = NULL, *tmpValues;
6771   PetscBool       anyConstrained = PETSC_FALSE;
6772 
6773   PetscFunctionBegin;
6774   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6775   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6776   PetscCall(PetscSectionGetNumFields(section, &numFields));
6777 
6778   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
6779   /* if there are point-to-point constraints */
6780   if (aSec) {
6781     PetscCall(PetscArrayzero(newOffsets, 32));
6782     PetscCall(ISGetIndices(aIS, &anchors));
6783     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
6784     /* figure out how many points are going to be in the new element matrix
6785      * (we allow double counting, because it's all just going to be summed
6786      * into the global matrix anyway) */
6787     for (p = 0; p < 2 * numPoints; p += 2) {
6788       PetscInt b    = points[p];
6789       PetscInt bDof = 0, bSecDof;
6790 
6791       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6792       if (!bSecDof) continue;
6793       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6794       if (bDof) {
6795         /* this point is constrained */
6796         /* it is going to be replaced by its anchors */
6797         PetscInt bOff, q;
6798 
6799         anyConstrained = PETSC_TRUE;
6800         newNumPoints += bDof;
6801         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6802         for (q = 0; q < bDof; q++) {
6803           PetscInt a = anchors[bOff + q];
6804           PetscInt aDof;
6805 
6806           PetscCall(PetscSectionGetDof(section, a, &aDof));
6807           newNumIndices += aDof;
6808           for (f = 0; f < numFields; ++f) {
6809             PetscInt fDof;
6810 
6811             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6812             newOffsets[f + 1] += fDof;
6813           }
6814         }
6815       } else {
6816         /* this point is not constrained */
6817         newNumPoints++;
6818         newNumIndices += bSecDof;
6819         for (f = 0; f < numFields; ++f) {
6820           PetscInt fDof;
6821 
6822           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6823           newOffsets[f + 1] += fDof;
6824         }
6825       }
6826     }
6827   }
6828   if (!anyConstrained) {
6829     if (outNumPoints) *outNumPoints = 0;
6830     if (outNumIndices) *outNumIndices = 0;
6831     if (outPoints) *outPoints = NULL;
6832     if (outValues) *outValues = NULL;
6833     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
6834     PetscFunctionReturn(0);
6835   }
6836 
6837   if (outNumPoints) *outNumPoints = newNumPoints;
6838   if (outNumIndices) *outNumIndices = newNumIndices;
6839 
6840   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
6841 
6842   if (!outPoints && !outValues) {
6843     if (offsets) {
6844       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
6845     }
6846     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
6847     PetscFunctionReturn(0);
6848   }
6849 
6850   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6851 
6852   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6853 
6854   /* workspaces */
6855   if (numFields) {
6856     for (f = 0; f < numFields; f++) {
6857       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
6858       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
6859     }
6860   } else {
6861     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
6862     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
6863   }
6864 
6865   /* get workspaces for the point-to-point matrices */
6866   if (numFields) {
6867     PetscInt totalOffset, totalMatOffset;
6868 
6869     for (p = 0; p < numPoints; p++) {
6870       PetscInt b    = points[2 * p];
6871       PetscInt bDof = 0, bSecDof;
6872 
6873       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6874       if (!bSecDof) {
6875         for (f = 0; f < numFields; f++) {
6876           newPointOffsets[f][p + 1] = 0;
6877           pointMatOffsets[f][p + 1] = 0;
6878         }
6879         continue;
6880       }
6881       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6882       if (bDof) {
6883         for (f = 0; f < numFields; f++) {
6884           PetscInt fDof, q, bOff, allFDof = 0;
6885 
6886           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6887           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6888           for (q = 0; q < bDof; q++) {
6889             PetscInt a = anchors[bOff + q];
6890             PetscInt aFDof;
6891 
6892             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6893             allFDof += aFDof;
6894           }
6895           newPointOffsets[f][p + 1] = allFDof;
6896           pointMatOffsets[f][p + 1] = fDof * allFDof;
6897         }
6898       } else {
6899         for (f = 0; f < numFields; f++) {
6900           PetscInt fDof;
6901 
6902           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6903           newPointOffsets[f][p + 1] = fDof;
6904           pointMatOffsets[f][p + 1] = 0;
6905         }
6906       }
6907     }
6908     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6909       newPointOffsets[f][0] = totalOffset;
6910       pointMatOffsets[f][0] = totalMatOffset;
6911       for (p = 0; p < numPoints; p++) {
6912         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
6913         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
6914       }
6915       totalOffset    = newPointOffsets[f][numPoints];
6916       totalMatOffset = pointMatOffsets[f][numPoints];
6917       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
6918     }
6919   } else {
6920     for (p = 0; p < numPoints; p++) {
6921       PetscInt b    = points[2 * p];
6922       PetscInt bDof = 0, bSecDof;
6923 
6924       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6925       if (!bSecDof) {
6926         newPointOffsets[0][p + 1] = 0;
6927         pointMatOffsets[0][p + 1] = 0;
6928         continue;
6929       }
6930       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6931       if (bDof) {
6932         PetscInt bOff, q, allDof = 0;
6933 
6934         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6935         for (q = 0; q < bDof; q++) {
6936           PetscInt a = anchors[bOff + q], aDof;
6937 
6938           PetscCall(PetscSectionGetDof(section, a, &aDof));
6939           allDof += aDof;
6940         }
6941         newPointOffsets[0][p + 1] = allDof;
6942         pointMatOffsets[0][p + 1] = bSecDof * allDof;
6943       } else {
6944         newPointOffsets[0][p + 1] = bSecDof;
6945         pointMatOffsets[0][p + 1] = 0;
6946       }
6947     }
6948     newPointOffsets[0][0] = 0;
6949     pointMatOffsets[0][0] = 0;
6950     for (p = 0; p < numPoints; p++) {
6951       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
6952       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
6953     }
6954     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
6955   }
6956 
6957   /* output arrays */
6958   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
6959 
6960   /* get the point-to-point matrices; construct newPoints */
6961   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6962   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6963   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
6964   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
6965   if (numFields) {
6966     for (p = 0, newP = 0; p < numPoints; p++) {
6967       PetscInt b    = points[2 * p];
6968       PetscInt o    = points[2 * p + 1];
6969       PetscInt bDof = 0, bSecDof;
6970 
6971       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6972       if (!bSecDof) continue;
6973       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6974       if (bDof) {
6975         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6976 
6977         fStart[0] = 0;
6978         fEnd[0]   = 0;
6979         for (f = 0; f < numFields; f++) {
6980           PetscInt fDof;
6981 
6982           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
6983           fStart[f + 1] = fStart[f] + fDof;
6984           fEnd[f + 1]   = fStart[f + 1];
6985         }
6986         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
6987         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
6988 
6989         fAnchorStart[0] = 0;
6990         fAnchorEnd[0]   = 0;
6991         for (f = 0; f < numFields; f++) {
6992           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
6993 
6994           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
6995           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
6996         }
6997         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6998         for (q = 0; q < bDof; q++) {
6999           PetscInt a = anchors[bOff + q], aOff;
7000 
7001           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7002           newPoints[2 * (newP + q)]     = a;
7003           newPoints[2 * (newP + q) + 1] = 0;
7004           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7005           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7006         }
7007         newP += bDof;
7008 
7009         if (outValues) {
7010           /* get the point-to-point submatrix */
7011           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]));
7012         }
7013       } else {
7014         newPoints[2 * newP]     = b;
7015         newPoints[2 * newP + 1] = o;
7016         newP++;
7017       }
7018     }
7019   } else {
7020     for (p = 0; p < numPoints; p++) {
7021       PetscInt b    = points[2 * p];
7022       PetscInt o    = points[2 * p + 1];
7023       PetscInt bDof = 0, bSecDof;
7024 
7025       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7026       if (!bSecDof) continue;
7027       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7028       if (bDof) {
7029         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7030 
7031         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7032         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7033 
7034         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7035         for (q = 0; q < bDof; q++) {
7036           PetscInt a = anchors[bOff + q], aOff;
7037 
7038           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7039 
7040           newPoints[2 * (newP + q)]     = a;
7041           newPoints[2 * (newP + q) + 1] = 0;
7042           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7043           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7044         }
7045         newP += bDof;
7046 
7047         /* get the point-to-point submatrix */
7048         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7049       } else {
7050         newPoints[2 * newP]     = b;
7051         newPoints[2 * newP + 1] = o;
7052         newP++;
7053       }
7054     }
7055   }
7056 
7057   if (outValues) {
7058     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7059     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7060     /* multiply constraints on the right */
7061     if (numFields) {
7062       for (f = 0; f < numFields; f++) {
7063         PetscInt oldOff = offsets[f];
7064 
7065         for (p = 0; p < numPoints; p++) {
7066           PetscInt cStart = newPointOffsets[f][p];
7067           PetscInt b      = points[2 * p];
7068           PetscInt c, r, k;
7069           PetscInt dof;
7070 
7071           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7072           if (!dof) continue;
7073           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7074             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7075             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7076 
7077             for (r = 0; r < numIndices; r++) {
7078               for (c = 0; c < nCols; c++) {
7079                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7080               }
7081             }
7082           } else {
7083             /* copy this column as is */
7084             for (r = 0; r < numIndices; r++) {
7085               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7086             }
7087           }
7088           oldOff += dof;
7089         }
7090       }
7091     } else {
7092       PetscInt oldOff = 0;
7093       for (p = 0; p < numPoints; p++) {
7094         PetscInt cStart = newPointOffsets[0][p];
7095         PetscInt b      = points[2 * p];
7096         PetscInt c, r, k;
7097         PetscInt dof;
7098 
7099         PetscCall(PetscSectionGetDof(section, b, &dof));
7100         if (!dof) continue;
7101         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7102           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7103           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7104 
7105           for (r = 0; r < numIndices; r++) {
7106             for (c = 0; c < nCols; c++) {
7107               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7108             }
7109           }
7110         } else {
7111           /* copy this column as is */
7112           for (r = 0; r < numIndices; r++) {
7113             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7114           }
7115         }
7116         oldOff += dof;
7117       }
7118     }
7119 
7120     if (multiplyLeft) {
7121       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7122       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7123       /* multiply constraints transpose on the left */
7124       if (numFields) {
7125         for (f = 0; f < numFields; f++) {
7126           PetscInt oldOff = offsets[f];
7127 
7128           for (p = 0; p < numPoints; p++) {
7129             PetscInt rStart = newPointOffsets[f][p];
7130             PetscInt b      = points[2 * p];
7131             PetscInt c, r, k;
7132             PetscInt dof;
7133 
7134             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7135             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7136               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7137               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7138 
7139               for (r = 0; r < nRows; r++) {
7140                 for (c = 0; c < newNumIndices; c++) {
7141                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7142                 }
7143               }
7144             } else {
7145               /* copy this row as is */
7146               for (r = 0; r < dof; r++) {
7147                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7148               }
7149             }
7150             oldOff += dof;
7151           }
7152         }
7153       } else {
7154         PetscInt oldOff = 0;
7155 
7156         for (p = 0; p < numPoints; p++) {
7157           PetscInt rStart = newPointOffsets[0][p];
7158           PetscInt b      = points[2 * p];
7159           PetscInt c, r, k;
7160           PetscInt dof;
7161 
7162           PetscCall(PetscSectionGetDof(section, b, &dof));
7163           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7164             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7165             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7166 
7167             for (r = 0; r < nRows; r++) {
7168               for (c = 0; c < newNumIndices; c++) {
7169                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7170               }
7171             }
7172           } else {
7173             /* copy this row as is */
7174             for (r = 0; r < dof; r++) {
7175               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7176             }
7177           }
7178           oldOff += dof;
7179         }
7180       }
7181 
7182       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7183     } else {
7184       newValues = tmpValues;
7185     }
7186   }
7187 
7188   /* clean up */
7189   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7190   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7191 
7192   if (numFields) {
7193     for (f = 0; f < numFields; f++) {
7194       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7195       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7196       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7197     }
7198   } else {
7199     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7200     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7201     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7202   }
7203   PetscCall(ISRestoreIndices(aIS, &anchors));
7204 
7205   /* output */
7206   if (outPoints) {
7207     *outPoints = newPoints;
7208   } else {
7209     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7210   }
7211   if (outValues) *outValues = newValues;
7212   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7213   PetscFunctionReturn(0);
7214 }
7215 
7216 /*@C
7217   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7218 
7219   Not collective
7220 
7221   Input Parameters:
7222 + dm         - The DM
7223 . section    - The PetscSection describing the points (a local section)
7224 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7225 . point      - The point defining the closure
7226 - useClPerm  - Use the closure point permutation if available
7227 
7228   Output Parameters:
7229 + numIndices - The number of dof indices in the closure of point with the input sections
7230 . indices    - The dof indices
7231 . outOffsets - Array to write the field offsets into, or NULL
7232 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7233 
7234   Notes:
7235   Must call DMPlexRestoreClosureIndices() to free allocated memory
7236 
7237   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7238   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7239   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7240   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7241   indices (with the above semantics) are implied.
7242 
7243   Level: advanced
7244 
7245 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7246 @*/
7247 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[]) {
7248   /* Closure ordering */
7249   PetscSection        clSection;
7250   IS                  clPoints;
7251   const PetscInt     *clp;
7252   PetscInt           *points;
7253   const PetscInt     *clperm    = NULL;
7254   /* Dof permutation and sign flips */
7255   const PetscInt    **perms[32] = {NULL};
7256   const PetscScalar **flips[32] = {NULL};
7257   PetscScalar        *valCopy   = NULL;
7258   /* Hanging node constraints */
7259   PetscInt           *pointsC   = NULL;
7260   PetscScalar        *valuesC   = NULL;
7261   PetscInt            NclC, NiC;
7262 
7263   PetscInt *idx;
7264   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7265   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7266 
7267   PetscFunctionBeginHot;
7268   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7269   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7270   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7271   if (numIndices) PetscValidIntPointer(numIndices, 6);
7272   if (indices) PetscValidPointer(indices, 7);
7273   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7274   if (values) PetscValidPointer(values, 9);
7275   PetscCall(PetscSectionGetNumFields(section, &Nf));
7276   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7277   PetscCall(PetscArrayzero(offsets, 32));
7278   /* 1) Get points in closure */
7279   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7280   if (useClPerm) {
7281     PetscInt depth, clsize;
7282     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7283     for (clsize = 0, p = 0; p < Ncl; p++) {
7284       PetscInt dof;
7285       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7286       clsize += dof;
7287     }
7288     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7289   }
7290   /* 2) Get number of indices on these points and field offsets from section */
7291   for (p = 0; p < Ncl * 2; p += 2) {
7292     PetscInt dof, fdof;
7293 
7294     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7295     for (f = 0; f < Nf; ++f) {
7296       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7297       offsets[f + 1] += fdof;
7298     }
7299     Ni += dof;
7300   }
7301   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7302   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7303   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7304   for (f = 0; f < PetscMax(1, Nf); ++f) {
7305     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7306     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7307     /* may need to apply sign changes to the element matrix */
7308     if (values && flips[f]) {
7309       PetscInt foffset = offsets[f];
7310 
7311       for (p = 0; p < Ncl; ++p) {
7312         PetscInt           pnt  = points[2 * p], fdof;
7313         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7314 
7315         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7316         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7317         if (flip) {
7318           PetscInt i, j, k;
7319 
7320           if (!valCopy) {
7321             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7322             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7323             *values = valCopy;
7324           }
7325           for (i = 0; i < fdof; ++i) {
7326             PetscScalar fval = flip[i];
7327 
7328             for (k = 0; k < Ni; ++k) {
7329               valCopy[Ni * (foffset + i) + k] *= fval;
7330               valCopy[Ni * k + (foffset + i)] *= fval;
7331             }
7332           }
7333         }
7334         foffset += fdof;
7335       }
7336     }
7337   }
7338   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7339   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7340   if (NclC) {
7341     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7342     for (f = 0; f < PetscMax(1, Nf); ++f) {
7343       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7344       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7345     }
7346     for (f = 0; f < PetscMax(1, Nf); ++f) {
7347       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7348       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7349     }
7350     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7351     Ncl    = NclC;
7352     Ni     = NiC;
7353     points = pointsC;
7354     if (values) *values = valuesC;
7355   }
7356   /* 5) Calculate indices */
7357   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7358   if (Nf) {
7359     PetscInt  idxOff;
7360     PetscBool useFieldOffsets;
7361 
7362     if (outOffsets) {
7363       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7364     }
7365     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7366     if (useFieldOffsets) {
7367       for (p = 0; p < Ncl; ++p) {
7368         const PetscInt pnt = points[p * 2];
7369 
7370         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7371       }
7372     } else {
7373       for (p = 0; p < Ncl; ++p) {
7374         const PetscInt pnt = points[p * 2];
7375 
7376         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7377         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7378          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7379          * global section. */
7380         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7381       }
7382     }
7383   } else {
7384     PetscInt off = 0, idxOff;
7385 
7386     for (p = 0; p < Ncl; ++p) {
7387       const PetscInt  pnt  = points[p * 2];
7388       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7389 
7390       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7391       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7392        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7393       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7394     }
7395   }
7396   /* 6) Cleanup */
7397   for (f = 0; f < PetscMax(1, Nf); ++f) {
7398     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7399     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7400   }
7401   if (NclC) {
7402     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7403   } else {
7404     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7405   }
7406 
7407   if (numIndices) *numIndices = Ni;
7408   if (indices) *indices = idx;
7409   PetscFunctionReturn(0);
7410 }
7411 
7412 /*@C
7413   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7414 
7415   Not collective
7416 
7417   Input Parameters:
7418 + dm         - The DM
7419 . section    - The PetscSection describing the points (a local section)
7420 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7421 . point      - The point defining the closure
7422 - useClPerm  - Use the closure point permutation if available
7423 
7424   Output Parameters:
7425 + numIndices - The number of dof indices in the closure of point with the input sections
7426 . indices    - The dof indices
7427 . outOffsets - Array to write the field offsets into, or NULL
7428 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7429 
7430   Notes:
7431   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7432 
7433   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7434   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7435   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7436   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7437   indices (with the above semantics) are implied.
7438 
7439   Level: advanced
7440 
7441 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7442 @*/
7443 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[]) {
7444   PetscFunctionBegin;
7445   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7446   PetscValidPointer(indices, 7);
7447   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7448   PetscFunctionReturn(0);
7449 }
7450 
7451 /*@C
7452   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7453 
7454   Not collective
7455 
7456   Input Parameters:
7457 + dm - The DM
7458 . section - The section describing the layout in v, or NULL to use the default section
7459 . globalSection - The section describing the layout in v, or NULL to use the default global section
7460 . A - The matrix
7461 . point - The point in the DM
7462 . values - The array of values
7463 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7464 
7465   Fortran Notes:
7466   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7467 
7468   Level: intermediate
7469 
7470 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7471 @*/
7472 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode) {
7473   DM_Plex           *mesh = (DM_Plex *)dm->data;
7474   PetscInt          *indices;
7475   PetscInt           numIndices;
7476   const PetscScalar *valuesOrig = values;
7477   PetscErrorCode     ierr;
7478 
7479   PetscFunctionBegin;
7480   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7481   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7482   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7483   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7484   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7485   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7486 
7487   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7488 
7489   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7490   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7491   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7492   if (ierr) {
7493     PetscMPIInt rank;
7494 
7495     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7496     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7497     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7498     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7499     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7500     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7501   }
7502   if (mesh->printFEM > 1) {
7503     PetscInt i;
7504     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7505     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7506     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7507   }
7508 
7509   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7510   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7511   PetscFunctionReturn(0);
7512 }
7513 
7514 /*@C
7515   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7516 
7517   Not collective
7518 
7519   Input Parameters:
7520 + dmRow - The DM for the row fields
7521 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7522 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7523 . dmCol - The DM for the column fields
7524 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7525 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7526 . A - The matrix
7527 . point - The point in the DMs
7528 . values - The array of values
7529 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7530 
7531   Level: intermediate
7532 
7533 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7534 @*/
7535 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode) {
7536   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7537   PetscInt          *indicesRow, *indicesCol;
7538   PetscInt           numIndicesRow, numIndicesCol;
7539   const PetscScalar *valuesOrig = values;
7540   PetscErrorCode     ierr;
7541 
7542   PetscFunctionBegin;
7543   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7544   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7545   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7546   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7547   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7548   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7549   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7550   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7551   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7552   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7553   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7554 
7555   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7556   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7557 
7558   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7559   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7560   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7561   if (ierr) {
7562     PetscMPIInt rank;
7563 
7564     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7565     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7566     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7567     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7568     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7569     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7570   }
7571 
7572   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7573   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7574   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7575   PetscFunctionReturn(0);
7576 }
7577 
7578 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode) {
7579   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7580   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7581   PetscInt       *cpoints = NULL;
7582   PetscInt       *findices, *cindices;
7583   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7584   PetscInt        foffsets[32], coffsets[32];
7585   DMPolytopeType  ct;
7586   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7587   PetscErrorCode  ierr;
7588 
7589   PetscFunctionBegin;
7590   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7591   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7592   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7593   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7594   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7595   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7596   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7597   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7598   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7599   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7600   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7601   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7602   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7603   PetscCall(PetscArrayzero(foffsets, 32));
7604   PetscCall(PetscArrayzero(coffsets, 32));
7605   /* Column indices */
7606   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7607   maxFPoints = numCPoints;
7608   /* Compress out points not in the section */
7609   /*   TODO: Squeeze out points with 0 dof as well */
7610   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7611   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7612     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7613       cpoints[q * 2]     = cpoints[p];
7614       cpoints[q * 2 + 1] = cpoints[p + 1];
7615       ++q;
7616     }
7617   }
7618   numCPoints = q;
7619   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7620     PetscInt fdof;
7621 
7622     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7623     if (!dof) continue;
7624     for (f = 0; f < numFields; ++f) {
7625       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7626       coffsets[f + 1] += fdof;
7627     }
7628     numCIndices += dof;
7629   }
7630   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7631   /* Row indices */
7632   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7633   {
7634     DMPlexTransform tr;
7635     DMPolytopeType *rct;
7636     PetscInt       *rsize, *rcone, *rornt, Nt;
7637 
7638     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7639     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7640     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7641     numSubcells = rsize[Nt - 1];
7642     PetscCall(DMPlexTransformDestroy(&tr));
7643   }
7644   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7645   for (r = 0, q = 0; r < numSubcells; ++r) {
7646     /* TODO Map from coarse to fine cells */
7647     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7648     /* Compress out points not in the section */
7649     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7650     for (p = 0; p < numFPoints * 2; p += 2) {
7651       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7652         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7653         if (!dof) continue;
7654         for (s = 0; s < q; ++s)
7655           if (fpoints[p] == ftotpoints[s * 2]) break;
7656         if (s < q) continue;
7657         ftotpoints[q * 2]     = fpoints[p];
7658         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7659         ++q;
7660       }
7661     }
7662     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7663   }
7664   numFPoints = q;
7665   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7666     PetscInt fdof;
7667 
7668     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7669     if (!dof) continue;
7670     for (f = 0; f < numFields; ++f) {
7671       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7672       foffsets[f + 1] += fdof;
7673     }
7674     numFIndices += dof;
7675   }
7676   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7677 
7678   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7679   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7680   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7681   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7682   if (numFields) {
7683     const PetscInt **permsF[32] = {NULL};
7684     const PetscInt **permsC[32] = {NULL};
7685 
7686     for (f = 0; f < numFields; f++) {
7687       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7688       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7689     }
7690     for (p = 0; p < numFPoints; p++) {
7691       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7692       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7693     }
7694     for (p = 0; p < numCPoints; p++) {
7695       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7696       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7697     }
7698     for (f = 0; f < numFields; f++) {
7699       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7700       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7701     }
7702   } else {
7703     const PetscInt **permsF = NULL;
7704     const PetscInt **permsC = NULL;
7705 
7706     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7707     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7708     for (p = 0, off = 0; p < numFPoints; p++) {
7709       const PetscInt *perm = permsF ? permsF[p] : NULL;
7710 
7711       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7712       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7713     }
7714     for (p = 0, off = 0; p < numCPoints; p++) {
7715       const PetscInt *perm = permsC ? permsC[p] : NULL;
7716 
7717       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7718       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7719     }
7720     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7721     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7722   }
7723   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7724   /* TODO: flips */
7725   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7726   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7727   if (ierr) {
7728     PetscMPIInt rank;
7729 
7730     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7731     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7732     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7733     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7734     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7735   }
7736   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
7737   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7738   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7739   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7740   PetscFunctionReturn(0);
7741 }
7742 
7743 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[]) {
7744   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7745   PetscInt       *cpoints = NULL;
7746   PetscInt        foffsets[32], coffsets[32];
7747   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7748   DMPolytopeType  ct;
7749   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7750 
7751   PetscFunctionBegin;
7752   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7753   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7754   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7755   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7756   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7757   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7758   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7759   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7760   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7761   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7762   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7763   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7764   PetscCall(PetscArrayzero(foffsets, 32));
7765   PetscCall(PetscArrayzero(coffsets, 32));
7766   /* Column indices */
7767   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7768   maxFPoints = numCPoints;
7769   /* Compress out points not in the section */
7770   /*   TODO: Squeeze out points with 0 dof as well */
7771   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7772   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7773     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7774       cpoints[q * 2]     = cpoints[p];
7775       cpoints[q * 2 + 1] = cpoints[p + 1];
7776       ++q;
7777     }
7778   }
7779   numCPoints = q;
7780   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7781     PetscInt fdof;
7782 
7783     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7784     if (!dof) continue;
7785     for (f = 0; f < numFields; ++f) {
7786       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7787       coffsets[f + 1] += fdof;
7788     }
7789     numCIndices += dof;
7790   }
7791   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7792   /* Row indices */
7793   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7794   {
7795     DMPlexTransform tr;
7796     DMPolytopeType *rct;
7797     PetscInt       *rsize, *rcone, *rornt, Nt;
7798 
7799     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7800     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7801     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7802     numSubcells = rsize[Nt - 1];
7803     PetscCall(DMPlexTransformDestroy(&tr));
7804   }
7805   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7806   for (r = 0, q = 0; r < numSubcells; ++r) {
7807     /* TODO Map from coarse to fine cells */
7808     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7809     /* Compress out points not in the section */
7810     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7811     for (p = 0; p < numFPoints * 2; p += 2) {
7812       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7813         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7814         if (!dof) continue;
7815         for (s = 0; s < q; ++s)
7816           if (fpoints[p] == ftotpoints[s * 2]) break;
7817         if (s < q) continue;
7818         ftotpoints[q * 2]     = fpoints[p];
7819         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7820         ++q;
7821       }
7822     }
7823     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7824   }
7825   numFPoints = q;
7826   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7827     PetscInt fdof;
7828 
7829     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7830     if (!dof) continue;
7831     for (f = 0; f < numFields; ++f) {
7832       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7833       foffsets[f + 1] += fdof;
7834     }
7835     numFIndices += dof;
7836   }
7837   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7838 
7839   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7840   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7841   if (numFields) {
7842     const PetscInt **permsF[32] = {NULL};
7843     const PetscInt **permsC[32] = {NULL};
7844 
7845     for (f = 0; f < numFields; f++) {
7846       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7847       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7848     }
7849     for (p = 0; p < numFPoints; p++) {
7850       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7851       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7852     }
7853     for (p = 0; p < numCPoints; p++) {
7854       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7855       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7856     }
7857     for (f = 0; f < numFields; f++) {
7858       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7859       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7860     }
7861   } else {
7862     const PetscInt **permsF = NULL;
7863     const PetscInt **permsC = NULL;
7864 
7865     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7866     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7867     for (p = 0, off = 0; p < numFPoints; p++) {
7868       const PetscInt *perm = permsF ? permsF[p] : NULL;
7869 
7870       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7871       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7872     }
7873     for (p = 0, off = 0; p < numCPoints; p++) {
7874       const PetscInt *perm = permsC ? permsC[p] : NULL;
7875 
7876       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7877       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7878     }
7879     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7880     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7881   }
7882   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
7883   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7884   PetscFunctionReturn(0);
7885 }
7886 
7887 /*@C
7888   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7889 
7890   Input Parameter:
7891 . dm   - The DMPlex object
7892 
7893   Output Parameter:
7894 . cellHeight - The height of a cell
7895 
7896   Level: developer
7897 
7898 .seealso `DMPlexSetVTKCellHeight()`
7899 @*/
7900 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight) {
7901   DM_Plex *mesh = (DM_Plex *)dm->data;
7902 
7903   PetscFunctionBegin;
7904   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7905   PetscValidIntPointer(cellHeight, 2);
7906   *cellHeight = mesh->vtkCellHeight;
7907   PetscFunctionReturn(0);
7908 }
7909 
7910 /*@C
7911   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7912 
7913   Input Parameters:
7914 + dm   - The DMPlex object
7915 - cellHeight - The height of a cell
7916 
7917   Level: developer
7918 
7919 .seealso `DMPlexGetVTKCellHeight()`
7920 @*/
7921 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight) {
7922   DM_Plex *mesh = (DM_Plex *)dm->data;
7923 
7924   PetscFunctionBegin;
7925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7926   mesh->vtkCellHeight = cellHeight;
7927   PetscFunctionReturn(0);
7928 }
7929 
7930 /*@
7931   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7932 
7933   Input Parameter:
7934 . dm - The DMPlex object
7935 
7936   Output Parameters:
7937 + gcStart - The first ghost cell, or NULL
7938 - gcEnd   - The upper bound on ghost cells, or NULL
7939 
7940   Level: advanced
7941 
7942 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
7943 @*/
7944 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd) {
7945   DMLabel ctLabel;
7946 
7947   PetscFunctionBegin;
7948   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7949   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
7950   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
7951   // Reset label for fast lookup
7952   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
7953   PetscFunctionReturn(0);
7954 }
7955 
7956 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering) {
7957   PetscSection section, globalSection;
7958   PetscInt    *numbers, p;
7959 
7960   PetscFunctionBegin;
7961   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf));
7962   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
7963   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
7964   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
7965   PetscCall(PetscSectionSetUp(section));
7966   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
7967   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
7968   for (p = pStart; p < pEnd; ++p) {
7969     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
7970     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
7971     else numbers[p - pStart] += shift;
7972   }
7973   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
7974   if (globalSize) {
7975     PetscLayout layout;
7976     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
7977     PetscCall(PetscLayoutGetSize(layout, globalSize));
7978     PetscCall(PetscLayoutDestroy(&layout));
7979   }
7980   PetscCall(PetscSectionDestroy(&section));
7981   PetscCall(PetscSectionDestroy(&globalSection));
7982   PetscFunctionReturn(0);
7983 }
7984 
7985 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers) {
7986   PetscInt cellHeight, cStart, cEnd;
7987 
7988   PetscFunctionBegin;
7989   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
7990   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
7991   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
7992   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
7993   PetscFunctionReturn(0);
7994 }
7995 
7996 /*@
7997   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
7998 
7999   Input Parameter:
8000 . dm   - The DMPlex object
8001 
8002   Output Parameter:
8003 . globalCellNumbers - Global cell numbers for all cells on this process
8004 
8005   Level: developer
8006 
8007 .seealso `DMPlexGetVertexNumbering()`
8008 @*/
8009 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers) {
8010   DM_Plex *mesh = (DM_Plex *)dm->data;
8011 
8012   PetscFunctionBegin;
8013   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8014   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8015   *globalCellNumbers = mesh->globalCellNumbers;
8016   PetscFunctionReturn(0);
8017 }
8018 
8019 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers) {
8020   PetscInt vStart, vEnd;
8021 
8022   PetscFunctionBegin;
8023   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8024   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8025   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8026   PetscFunctionReturn(0);
8027 }
8028 
8029 /*@
8030   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8031 
8032   Input Parameter:
8033 . dm   - The DMPlex object
8034 
8035   Output Parameter:
8036 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8037 
8038   Level: developer
8039 
8040 .seealso `DMPlexGetCellNumbering()`
8041 @*/
8042 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers) {
8043   DM_Plex *mesh = (DM_Plex *)dm->data;
8044 
8045   PetscFunctionBegin;
8046   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8047   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8048   *globalVertexNumbers = mesh->globalVertexNumbers;
8049   PetscFunctionReturn(0);
8050 }
8051 
8052 /*@
8053   DMPlexCreatePointNumbering - Create a global numbering for all points.
8054 
8055   Collective on dm
8056 
8057   Input Parameter:
8058 . dm   - The DMPlex object
8059 
8060   Output Parameter:
8061 . globalPointNumbers - Global numbers for all points on this process
8062 
8063   Notes:
8064 
8065   The point numbering IS is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8066   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8067   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8068   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8069 
8070   The partitioned mesh is
8071 ```
8072  (2)--0--(3)--1--(4)    (1)--0--(2)
8073 ```
8074   and its global numbering is
8075 ```
8076   (3)--0--(4)--1--(5)--2--(6)
8077 ```
8078   Then the global numbering is provided as
8079 ```
8080 [0] Number of indices in set 5
8081 [0] 0 0
8082 [0] 1 1
8083 [0] 2 3
8084 [0] 3 4
8085 [0] 4 -6
8086 [1] Number of indices in set 3
8087 [1] 0 2
8088 [1] 1 5
8089 [1] 2 6
8090 ```
8091 
8092   Level: developer
8093 
8094 .seealso `DMPlexGetCellNumbering()`
8095 @*/
8096 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers) {
8097   IS        nums[4];
8098   PetscInt  depths[4], gdepths[4], starts[4];
8099   PetscInt  depth, d, shift = 0;
8100   PetscBool empty = PETSC_FALSE;
8101 
8102   PetscFunctionBegin;
8103   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8104   PetscCall(DMPlexGetDepth(dm, &depth));
8105   // For unstratified meshes use dim instead of depth
8106   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8107   // If any stratum is empty, we must mark all empty
8108   for (d = 0; d <= depth; ++d) {
8109     PetscInt end;
8110 
8111     depths[d] = depth - d;
8112     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8113     if (!(starts[d] - end)) empty = PETSC_TRUE;
8114   }
8115   if (empty)
8116     for (d = 0; d <= depth; ++d) {
8117       depths[d] = -1;
8118       starts[d] = -1;
8119     }
8120   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8121   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8122   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]);
8123   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8124   for (d = 0; d <= depth; ++d) {
8125     PetscInt pStart, pEnd, gsize;
8126 
8127     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8128     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8129     shift += gsize;
8130   }
8131   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8132   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8133   PetscFunctionReturn(0);
8134 }
8135 
8136 /*@
8137   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8138 
8139   Input Parameter:
8140 . dm - The DMPlex object
8141 
8142   Output Parameter:
8143 . ranks - The rank field
8144 
8145   Options Database Keys:
8146 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8147 
8148   Level: intermediate
8149 
8150 .seealso: `DMView()`
8151 @*/
8152 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks) {
8153   DM             rdm;
8154   PetscFE        fe;
8155   PetscScalar   *r;
8156   PetscMPIInt    rank;
8157   DMPolytopeType ct;
8158   PetscInt       dim, cStart, cEnd, c;
8159   PetscBool      simplex;
8160 
8161   PetscFunctionBeginUser;
8162   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8163   PetscValidPointer(ranks, 2);
8164   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8165   PetscCall(DMClone(dm, &rdm));
8166   PetscCall(DMGetDimension(rdm, &dim));
8167   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8168   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8169   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8170   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8171   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8172   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8173   PetscCall(PetscFEDestroy(&fe));
8174   PetscCall(DMCreateDS(rdm));
8175   PetscCall(DMCreateGlobalVector(rdm, ranks));
8176   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8177   PetscCall(VecGetArray(*ranks, &r));
8178   for (c = cStart; c < cEnd; ++c) {
8179     PetscScalar *lr;
8180 
8181     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8182     if (lr) *lr = rank;
8183   }
8184   PetscCall(VecRestoreArray(*ranks, &r));
8185   PetscCall(DMDestroy(&rdm));
8186   PetscFunctionReturn(0);
8187 }
8188 
8189 /*@
8190   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8191 
8192   Input Parameters:
8193 + dm    - The DMPlex
8194 - label - The DMLabel
8195 
8196   Output Parameter:
8197 . val - The label value field
8198 
8199   Options Database Keys:
8200 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8201 
8202   Level: intermediate
8203 
8204 .seealso: `DMView()`
8205 @*/
8206 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val) {
8207   DM           rdm;
8208   PetscFE      fe;
8209   PetscScalar *v;
8210   PetscInt     dim, cStart, cEnd, c;
8211 
8212   PetscFunctionBeginUser;
8213   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8214   PetscValidPointer(label, 2);
8215   PetscValidPointer(val, 3);
8216   PetscCall(DMClone(dm, &rdm));
8217   PetscCall(DMGetDimension(rdm, &dim));
8218   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8219   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8220   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8221   PetscCall(PetscFEDestroy(&fe));
8222   PetscCall(DMCreateDS(rdm));
8223   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8224   PetscCall(DMCreateGlobalVector(rdm, val));
8225   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8226   PetscCall(VecGetArray(*val, &v));
8227   for (c = cStart; c < cEnd; ++c) {
8228     PetscScalar *lv;
8229     PetscInt     cval;
8230 
8231     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8232     PetscCall(DMLabelGetValue(label, c, &cval));
8233     *lv = cval;
8234   }
8235   PetscCall(VecRestoreArray(*val, &v));
8236   PetscCall(DMDestroy(&rdm));
8237   PetscFunctionReturn(0);
8238 }
8239 
8240 /*@
8241   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8242 
8243   Input Parameter:
8244 . dm - The DMPlex object
8245 
8246   Notes:
8247   This is a useful diagnostic when creating meshes programmatically.
8248 
8249   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8250 
8251   Level: developer
8252 
8253 .seealso: `DMCreate()`, `DMSetFromOptions()`
8254 @*/
8255 PetscErrorCode DMPlexCheckSymmetry(DM dm) {
8256   PetscSection    coneSection, supportSection;
8257   const PetscInt *cone, *support;
8258   PetscInt        coneSize, c, supportSize, s;
8259   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8260   PetscBool       storagecheck = PETSC_TRUE;
8261 
8262   PetscFunctionBegin;
8263   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8264   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8265   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8266   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8267   /* Check that point p is found in the support of its cone points, and vice versa */
8268   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8269   for (p = pStart; p < pEnd; ++p) {
8270     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8271     PetscCall(DMPlexGetCone(dm, p, &cone));
8272     for (c = 0; c < coneSize; ++c) {
8273       PetscBool dup = PETSC_FALSE;
8274       PetscInt  d;
8275       for (d = c - 1; d >= 0; --d) {
8276         if (cone[c] == cone[d]) {
8277           dup = PETSC_TRUE;
8278           break;
8279         }
8280       }
8281       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8282       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8283       for (s = 0; s < supportSize; ++s) {
8284         if (support[s] == p) break;
8285       }
8286       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8287         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8288         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8289         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8290         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8291         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8292         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8293         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]);
8294         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8295       }
8296     }
8297     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8298     if (p != pp) {
8299       storagecheck = PETSC_FALSE;
8300       continue;
8301     }
8302     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8303     PetscCall(DMPlexGetSupport(dm, p, &support));
8304     for (s = 0; s < supportSize; ++s) {
8305       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8306       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8307       for (c = 0; c < coneSize; ++c) {
8308         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8309         if (cone[c] != pp) {
8310           c = 0;
8311           break;
8312         }
8313         if (cone[c] == p) break;
8314       }
8315       if (c >= coneSize) {
8316         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8317         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8318         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8319         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8320         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8321         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8322         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8323       }
8324     }
8325   }
8326   if (storagecheck) {
8327     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8328     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8329     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8330   }
8331   PetscFunctionReturn(0);
8332 }
8333 
8334 /*
8335   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.
8336 */
8337 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit) {
8338   DMPolytopeType  cct;
8339   PetscInt        ptpoints[4];
8340   const PetscInt *cone, *ccone, *ptcone;
8341   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8342 
8343   PetscFunctionBegin;
8344   *unsplit = 0;
8345   switch (ct) {
8346   case DM_POLYTOPE_POINT_PRISM_TENSOR: ptpoints[npt++] = c; break;
8347   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8348     PetscCall(DMPlexGetCone(dm, c, &cone));
8349     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8350     for (cp = 0; cp < coneSize; ++cp) {
8351       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8352       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8353     }
8354     break;
8355   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8356   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8357     PetscCall(DMPlexGetCone(dm, c, &cone));
8358     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8359     for (cp = 0; cp < coneSize; ++cp) {
8360       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8361       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8362       for (ccp = 0; ccp < cconeSize; ++ccp) {
8363         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8364         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8365           PetscInt p;
8366           for (p = 0; p < npt; ++p)
8367             if (ptpoints[p] == ccone[ccp]) break;
8368           if (p == npt) ptpoints[npt++] = ccone[ccp];
8369         }
8370       }
8371     }
8372     break;
8373   default: break;
8374   }
8375   for (pt = 0; pt < npt; ++pt) {
8376     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8377     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8378   }
8379   PetscFunctionReturn(0);
8380 }
8381 
8382 /*@
8383   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8384 
8385   Input Parameters:
8386 + dm - The DMPlex object
8387 - cellHeight - Normally 0
8388 
8389   Notes:
8390   This is a useful diagnostic when creating meshes programmatically.
8391   Currently applicable only to homogeneous simplex or tensor meshes.
8392 
8393   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8394 
8395   Level: developer
8396 
8397 .seealso: `DMCreate()`, `DMSetFromOptions()`
8398 @*/
8399 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight) {
8400   DMPlexInterpolatedFlag interp;
8401   DMPolytopeType         ct;
8402   PetscInt               vStart, vEnd, cStart, cEnd, c;
8403 
8404   PetscFunctionBegin;
8405   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8406   PetscCall(DMPlexIsInterpolated(dm, &interp));
8407   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8408   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8409   for (c = cStart; c < cEnd; ++c) {
8410     PetscInt *closure = NULL;
8411     PetscInt  coneSize, closureSize, cl, Nv = 0;
8412 
8413     PetscCall(DMPlexGetCellType(dm, c, &ct));
8414     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8415     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8416     if (interp == DMPLEX_INTERPOLATED_FULL) {
8417       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8418       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));
8419     }
8420     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8421     for (cl = 0; cl < closureSize * 2; cl += 2) {
8422       const PetscInt p = closure[cl];
8423       if ((p >= vStart) && (p < vEnd)) ++Nv;
8424     }
8425     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8426     /* Special Case: Tensor faces with identified vertices */
8427     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8428       PetscInt unsplit;
8429 
8430       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8431       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8432     }
8433     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));
8434   }
8435   PetscFunctionReturn(0);
8436 }
8437 
8438 /*@
8439   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8440 
8441   Collective
8442 
8443   Input Parameters:
8444 + dm - The DMPlex object
8445 - cellHeight - Normally 0
8446 
8447   Notes:
8448   This is a useful diagnostic when creating meshes programmatically.
8449   This routine is only relevant for meshes that are fully interpolated across all ranks.
8450   It will error out if a partially interpolated mesh is given on some rank.
8451   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8452 
8453   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8454 
8455   Level: developer
8456 
8457 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8458 @*/
8459 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight) {
8460   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8461   DMPlexInterpolatedFlag interpEnum;
8462 
8463   PetscFunctionBegin;
8464   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8465   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8466   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8467   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8468     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8469     PetscFunctionReturn(0);
8470   }
8471 
8472   PetscCall(DMGetDimension(dm, &dim));
8473   PetscCall(DMPlexGetDepth(dm, &depth));
8474   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8475   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8476     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8477     for (c = cStart; c < cEnd; ++c) {
8478       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8479       const DMPolytopeType *faceTypes;
8480       DMPolytopeType        ct;
8481       PetscInt              numFaces, coneSize, f;
8482       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8483 
8484       PetscCall(DMPlexGetCellType(dm, c, &ct));
8485       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8486       if (unsplit) continue;
8487       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8488       PetscCall(DMPlexGetCone(dm, c, &cone));
8489       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8490       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8491       for (cl = 0; cl < closureSize * 2; cl += 2) {
8492         const PetscInt p = closure[cl];
8493         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8494       }
8495       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8496       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);
8497       for (f = 0; f < numFaces; ++f) {
8498         DMPolytopeType fct;
8499         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8500 
8501         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8502         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8503         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8504           const PetscInt p = fclosure[cl];
8505           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8506         }
8507         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]);
8508         for (v = 0; v < fnumCorners; ++v) {
8509           if (fclosure[v] != faces[fOff + v]) {
8510             PetscInt v1;
8511 
8512             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8513             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8514             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8515             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8516             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8517             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]);
8518           }
8519         }
8520         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8521         fOff += faceSizes[f];
8522       }
8523       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8524       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8525     }
8526   }
8527   PetscFunctionReturn(0);
8528 }
8529 
8530 /*@
8531   DMPlexCheckGeometry - Check the geometry of mesh cells
8532 
8533   Input Parameter:
8534 . dm - The DMPlex object
8535 
8536   Notes:
8537   This is a useful diagnostic when creating meshes programmatically.
8538 
8539   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8540 
8541   Level: developer
8542 
8543 .seealso: `DMCreate()`, `DMSetFromOptions()`
8544 @*/
8545 PetscErrorCode DMPlexCheckGeometry(DM dm) {
8546   Vec       coordinates;
8547   PetscReal detJ, J[9], refVol = 1.0;
8548   PetscReal vol;
8549   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8550 
8551   PetscFunctionBegin;
8552   PetscCall(DMGetDimension(dm, &dim));
8553   PetscCall(DMGetCoordinateDim(dm, &dE));
8554   if (dim != dE) PetscFunctionReturn(0);
8555   PetscCall(DMPlexGetDepth(dm, &depth));
8556   for (d = 0; d < dim; ++d) refVol *= 2.0;
8557   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8558   /* Make sure local coordinates are created, because that step is collective */
8559   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8560   for (c = cStart; c < cEnd; ++c) {
8561     DMPolytopeType ct;
8562     PetscInt       unsplit;
8563     PetscBool      ignoreZeroVol = PETSC_FALSE;
8564 
8565     PetscCall(DMPlexGetCellType(dm, c, &ct));
8566     switch (ct) {
8567     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8568     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8569     case DM_POLYTOPE_QUAD_PRISM_TENSOR: ignoreZeroVol = PETSC_TRUE; break;
8570     default: break;
8571     }
8572     switch (ct) {
8573     case DM_POLYTOPE_TRI_PRISM:
8574     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8575     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8576     case DM_POLYTOPE_PYRAMID: continue;
8577     default: break;
8578     }
8579     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8580     if (unsplit) continue;
8581     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8582     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);
8583     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8584     /* This should work with periodicity since DG coordinates should be used */
8585     if (depth > 1) {
8586       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8587       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);
8588       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8589     }
8590   }
8591   PetscFunctionReturn(0);
8592 }
8593 
8594 /*@
8595   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8596 
8597   Collective
8598 
8599   Input Parameters:
8600 + dm - The DMPlex object
8601 - pointSF - The Point SF, or NULL for Point SF attached to DM
8602 
8603   Notes:
8604   This is mainly intended for debugging/testing purposes.
8605 
8606   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8607 
8608   Level: developer
8609 
8610 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8611 @*/
8612 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF) {
8613   PetscInt           l, nleaves, nroots, overlap;
8614   const PetscInt    *locals;
8615   const PetscSFNode *remotes;
8616   PetscBool          distributed;
8617   MPI_Comm           comm;
8618   PetscMPIInt        rank;
8619 
8620   PetscFunctionBegin;
8621   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8622   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8623   else pointSF = dm->sf;
8624   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8625   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8626   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8627   {
8628     PetscMPIInt mpiFlag;
8629 
8630     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8631     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8632   }
8633   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8634   PetscCall(DMPlexIsDistributed(dm, &distributed));
8635   if (!distributed) {
8636     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);
8637     PetscFunctionReturn(0);
8638   }
8639   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);
8640   PetscCall(DMPlexGetOverlap(dm, &overlap));
8641 
8642   /* Check SF graph is compatible with DMPlex chart */
8643   {
8644     PetscInt pStart, pEnd, maxLeaf;
8645 
8646     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8647     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8648     PetscCheck(pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
8649     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8650   }
8651 
8652   /* Check Point SF has no local points referenced */
8653   for (l = 0; l < nleaves; l++) {
8654     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);
8655   }
8656 
8657   /* Check there are no cells in interface */
8658   if (!overlap) {
8659     PetscInt cellHeight, cStart, cEnd;
8660 
8661     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8662     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8663     for (l = 0; l < nleaves; ++l) {
8664       const PetscInt point = locals ? locals[l] : l;
8665 
8666       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8667     }
8668   }
8669 
8670   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8671   {
8672     const PetscInt *rootdegree;
8673 
8674     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8675     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8676     for (l = 0; l < nleaves; ++l) {
8677       const PetscInt  point = locals ? locals[l] : l;
8678       const PetscInt *cone;
8679       PetscInt        coneSize, c, idx;
8680 
8681       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8682       PetscCall(DMPlexGetCone(dm, point, &cone));
8683       for (c = 0; c < coneSize; ++c) {
8684         if (!rootdegree[cone[c]]) {
8685           if (locals) {
8686             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8687           } else {
8688             idx = (cone[c] < nleaves) ? cone[c] : -1;
8689           }
8690           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8691         }
8692       }
8693     }
8694   }
8695   PetscFunctionReturn(0);
8696 }
8697 
8698 /*@
8699   DMPlexCheck - Perform various checks of Plex sanity
8700 
8701   Input Parameter:
8702 . dm - The DMPlex object
8703 
8704   Notes:
8705   This is a useful diagnostic when creating meshes programmatically.
8706 
8707   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8708 
8709   Currently does not include DMPlexCheckCellShape().
8710 
8711   Level: developer
8712 
8713 .seealso: DMCreate(), DMSetFromOptions()
8714 @*/
8715 PetscErrorCode DMPlexCheck(DM dm) {
8716   PetscInt cellHeight;
8717 
8718   PetscFunctionBegin;
8719   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8720   PetscCall(DMPlexCheckSymmetry(dm));
8721   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8722   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8723   PetscCall(DMPlexCheckGeometry(dm));
8724   PetscCall(DMPlexCheckPointSF(dm, NULL));
8725   PetscCall(DMPlexCheckInterfaceCones(dm));
8726   PetscFunctionReturn(0);
8727 }
8728 
8729 typedef struct cell_stats {
8730   PetscReal min, max, sum, squaresum;
8731   PetscInt  count;
8732 } cell_stats_t;
8733 
8734 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype) {
8735   PetscInt i, N = *len;
8736 
8737   for (i = 0; i < N; i++) {
8738     cell_stats_t *A = (cell_stats_t *)a;
8739     cell_stats_t *B = (cell_stats_t *)b;
8740 
8741     B->min = PetscMin(A->min, B->min);
8742     B->max = PetscMax(A->max, B->max);
8743     B->sum += A->sum;
8744     B->squaresum += A->squaresum;
8745     B->count += A->count;
8746   }
8747 }
8748 
8749 /*@
8750   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8751 
8752   Collective on dm
8753 
8754   Input Parameters:
8755 + dm        - The DMPlex object
8756 . output    - If true, statistics will be displayed on stdout
8757 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8758 
8759   Notes:
8760   This is mainly intended for debugging/testing purposes.
8761 
8762   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8763 
8764   Level: developer
8765 
8766 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8767 @*/
8768 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit) {
8769   DM           dmCoarse;
8770   cell_stats_t stats, globalStats;
8771   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
8772   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8773   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8774   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8775   PetscMPIInt  rank, size;
8776 
8777   PetscFunctionBegin;
8778   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8779   stats.min = PETSC_MAX_REAL;
8780   stats.max = PETSC_MIN_REAL;
8781   stats.sum = stats.squaresum = 0.;
8782   stats.count                 = 0;
8783 
8784   PetscCallMPI(MPI_Comm_size(comm, &size));
8785   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8786   PetscCall(DMGetCoordinateDim(dm, &cdim));
8787   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8788   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
8789   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8790   for (c = cStart; c < cEnd; c++) {
8791     PetscInt  i;
8792     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8793 
8794     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
8795     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8796     for (i = 0; i < PetscSqr(cdim); ++i) {
8797       frobJ += J[i] * J[i];
8798       frobInvJ += invJ[i] * invJ[i];
8799     }
8800     cond2 = frobJ * frobInvJ;
8801     cond  = PetscSqrtReal(cond2);
8802 
8803     stats.min = PetscMin(stats.min, cond);
8804     stats.max = PetscMax(stats.max, cond);
8805     stats.sum += cond;
8806     stats.squaresum += cond2;
8807     stats.count++;
8808     if (output && cond > limit) {
8809       PetscSection coordSection;
8810       Vec          coordsLocal;
8811       PetscScalar *coords = NULL;
8812       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8813 
8814       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8815       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8816       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8817       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
8818       for (i = 0; i < Nv / cdim; ++i) {
8819         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8820         for (d = 0; d < cdim; ++d) {
8821           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8822           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
8823         }
8824         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8825       }
8826       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8827       for (cl = 0; cl < clSize * 2; cl += 2) {
8828         const PetscInt edge = closure[cl];
8829 
8830         if ((edge >= eStart) && (edge < eEnd)) {
8831           PetscReal len;
8832 
8833           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8834           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
8835         }
8836       }
8837       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8838       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8839     }
8840   }
8841   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8842 
8843   if (size > 1) {
8844     PetscMPIInt  blockLengths[2] = {4, 1};
8845     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
8846     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
8847     MPI_Op       statReduce;
8848 
8849     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
8850     PetscCallMPI(MPI_Type_commit(&statType));
8851     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8852     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
8853     PetscCallMPI(MPI_Op_free(&statReduce));
8854     PetscCallMPI(MPI_Type_free(&statType));
8855   } else {
8856     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
8857   }
8858   if (rank == 0) {
8859     count = globalStats.count;
8860     min   = globalStats.min;
8861     max   = globalStats.max;
8862     mean  = globalStats.sum / globalStats.count;
8863     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
8864   }
8865 
8866   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));
8867   PetscCall(PetscFree2(J, invJ));
8868 
8869   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
8870   if (dmCoarse) {
8871     PetscBool isplex;
8872 
8873     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
8874     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
8875   }
8876   PetscFunctionReturn(0);
8877 }
8878 
8879 /*@
8880   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8881   orthogonal quality below given tolerance.
8882 
8883   Collective on dm
8884 
8885   Input Parameters:
8886 + dm   - The DMPlex object
8887 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8888 - atol - [0, 1] Absolute tolerance for tagging cells.
8889 
8890   Output Parameters:
8891 + OrthQual      - Vec containing orthogonal quality per cell
8892 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8893 
8894   Options Database Keys:
8895 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8896 supported.
8897 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8898 
8899   Notes:
8900   Orthogonal quality is given by the following formula:
8901 
8902   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8903 
8904   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
8905   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8906   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8907   calculating the cosine of the angle between these vectors.
8908 
8909   Orthogonal quality ranges from 1 (best) to 0 (worst).
8910 
8911   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8912   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8913 
8914   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8915 
8916   Level: intermediate
8917 
8918 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8919 @*/
8920 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel) {
8921   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8922   PetscInt              *idx;
8923   PetscScalar           *oqVals;
8924   const PetscScalar     *cellGeomArr, *faceGeomArr;
8925   PetscReal             *ci, *fi, *Ai;
8926   MPI_Comm               comm;
8927   Vec                    cellgeom, facegeom;
8928   DM                     dmFace, dmCell;
8929   IS                     glob;
8930   ISLocalToGlobalMapping ltog;
8931   PetscViewer            vwr;
8932 
8933   PetscFunctionBegin;
8934   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8935   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
8936   PetscValidPointer(OrthQual, 4);
8937   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
8938   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8939   PetscCall(DMGetDimension(dm, &nc));
8940   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8941   {
8942     DMPlexInterpolatedFlag interpFlag;
8943 
8944     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
8945     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
8946       PetscMPIInt rank;
8947 
8948       PetscCallMPI(MPI_Comm_rank(comm, &rank));
8949       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
8950     }
8951   }
8952   if (OrthQualLabel) {
8953     PetscValidPointer(OrthQualLabel, 5);
8954     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
8955     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
8956   } else {
8957     *OrthQualLabel = NULL;
8958   }
8959   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8960   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8961   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
8962   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
8963   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
8964   PetscCall(VecCreate(comm, OrthQual));
8965   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
8966   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
8967   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
8968   PetscCall(VecSetUp(*OrthQual));
8969   PetscCall(ISDestroy(&glob));
8970   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
8971   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
8972   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
8973   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
8974   PetscCall(VecGetDM(cellgeom, &dmCell));
8975   PetscCall(VecGetDM(facegeom, &dmFace));
8976   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
8977   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
8978     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
8979     PetscInt         cellarr[2], *adj = NULL;
8980     PetscScalar     *cArr, *fArr;
8981     PetscReal        minvalc = 1.0, minvalf = 1.0;
8982     PetscFVCellGeom *cg;
8983 
8984     idx[cellIter] = cell - cStart;
8985     cellarr[0]    = cell;
8986     /* Make indexing into cellGeom easier */
8987     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
8988     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
8989     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
8990     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
8991     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
8992       PetscInt         i;
8993       const PetscInt   neigh  = adj[cellneigh];
8994       PetscReal        normci = 0, normfi = 0, normai = 0;
8995       PetscFVCellGeom *cgneigh;
8996       PetscFVFaceGeom *fg;
8997 
8998       /* Don't count ourselves in the neighbor list */
8999       if (neigh == cell) continue;
9000       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9001       cellarr[1] = neigh;
9002       {
9003         PetscInt        numcovpts;
9004         const PetscInt *covpts;
9005 
9006         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9007         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9008         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9009       }
9010 
9011       /* Compute c_i, f_i and their norms */
9012       for (i = 0; i < nc; i++) {
9013         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9014         fi[i] = fg->centroid[i] - cg->centroid[i];
9015         Ai[i] = fg->normal[i];
9016         normci += PetscPowReal(ci[i], 2);
9017         normfi += PetscPowReal(fi[i], 2);
9018         normai += PetscPowReal(Ai[i], 2);
9019       }
9020       normci = PetscSqrtReal(normci);
9021       normfi = PetscSqrtReal(normfi);
9022       normai = PetscSqrtReal(normai);
9023 
9024       /* Normalize and compute for each face-cell-normal pair */
9025       for (i = 0; i < nc; i++) {
9026         ci[i] = ci[i] / normci;
9027         fi[i] = fi[i] / normfi;
9028         Ai[i] = Ai[i] / normai;
9029         /* PetscAbs because I don't know if normals are guaranteed to point out */
9030         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9031         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9032       }
9033       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9034       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9035     }
9036     PetscCall(PetscFree(adj));
9037     PetscCall(PetscFree2(cArr, fArr));
9038     /* Defer to cell if they're equal */
9039     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9040     if (OrthQualLabel) {
9041       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9042     }
9043   }
9044   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9045   PetscCall(VecAssemblyBegin(*OrthQual));
9046   PetscCall(VecAssemblyEnd(*OrthQual));
9047   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9048   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9049   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9050   if (OrthQualLabel) {
9051     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9052   }
9053   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9054   PetscCall(PetscViewerDestroy(&vwr));
9055   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9056   PetscFunctionReturn(0);
9057 }
9058 
9059 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9060  * interpolator construction */
9061 static PetscErrorCode DMGetFullDM(DM dm, DM *odm) {
9062   PetscSection section, newSection, gsection;
9063   PetscSF      sf;
9064   PetscBool    hasConstraints, ghasConstraints;
9065 
9066   PetscFunctionBegin;
9067   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9068   PetscValidPointer(odm, 2);
9069   PetscCall(DMGetLocalSection(dm, &section));
9070   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9071   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9072   if (!ghasConstraints) {
9073     PetscCall(PetscObjectReference((PetscObject)dm));
9074     *odm = dm;
9075     PetscFunctionReturn(0);
9076   }
9077   PetscCall(DMClone(dm, odm));
9078   PetscCall(DMCopyFields(dm, *odm));
9079   PetscCall(DMGetLocalSection(*odm, &newSection));
9080   PetscCall(DMGetPointSF(*odm, &sf));
9081   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9082   PetscCall(DMSetGlobalSection(*odm, gsection));
9083   PetscCall(PetscSectionDestroy(&gsection));
9084   PetscFunctionReturn(0);
9085 }
9086 
9087 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift) {
9088   DM        dmco, dmfo;
9089   Mat       interpo;
9090   Vec       rscale;
9091   Vec       cglobalo, clocal;
9092   Vec       fglobal, fglobalo, flocal;
9093   PetscBool regular;
9094 
9095   PetscFunctionBegin;
9096   PetscCall(DMGetFullDM(dmc, &dmco));
9097   PetscCall(DMGetFullDM(dmf, &dmfo));
9098   PetscCall(DMSetCoarseDM(dmfo, dmco));
9099   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9100   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9101   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9102   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9103   PetscCall(DMCreateLocalVector(dmc, &clocal));
9104   PetscCall(VecSet(cglobalo, 0.));
9105   PetscCall(VecSet(clocal, 0.));
9106   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9107   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9108   PetscCall(DMCreateLocalVector(dmf, &flocal));
9109   PetscCall(VecSet(fglobal, 0.));
9110   PetscCall(VecSet(fglobalo, 0.));
9111   PetscCall(VecSet(flocal, 0.));
9112   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9113   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9114   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9115   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9116   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9117   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9118   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9119   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9120   *shift = fglobal;
9121   PetscCall(VecDestroy(&flocal));
9122   PetscCall(VecDestroy(&fglobalo));
9123   PetscCall(VecDestroy(&clocal));
9124   PetscCall(VecDestroy(&cglobalo));
9125   PetscCall(VecDestroy(&rscale));
9126   PetscCall(MatDestroy(&interpo));
9127   PetscCall(DMDestroy(&dmfo));
9128   PetscCall(DMDestroy(&dmco));
9129   PetscFunctionReturn(0);
9130 }
9131 
9132 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol) {
9133   PetscObject shifto;
9134   Vec         shift;
9135 
9136   PetscFunctionBegin;
9137   if (!interp) {
9138     Vec rscale;
9139 
9140     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9141     PetscCall(VecDestroy(&rscale));
9142   } else {
9143     PetscCall(PetscObjectReference((PetscObject)interp));
9144   }
9145   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9146   if (!shifto) {
9147     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9148     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9149     shifto = (PetscObject)shift;
9150     PetscCall(VecDestroy(&shift));
9151   }
9152   shift = (Vec)shifto;
9153   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9154   PetscCall(VecAXPY(fineSol, 1.0, shift));
9155   PetscCall(MatDestroy(&interp));
9156   PetscFunctionReturn(0);
9157 }
9158 
9159 /* Pointwise interpolation
9160      Just code FEM for now
9161      u^f = I u^c
9162      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9163      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9164      I_{ij} = psi^f_i phi^c_j
9165 */
9166 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling) {
9167   PetscSection gsc, gsf;
9168   PetscInt     m, n;
9169   void        *ctx;
9170   DM           cdm;
9171   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9172 
9173   PetscFunctionBegin;
9174   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9175   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9176   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9177   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9178 
9179   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9180   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9181   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9182   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9183   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9184 
9185   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9186   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9187   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9188   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9189   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9190   if (scaling) {
9191     /* Use naive scaling */
9192     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9193   }
9194   PetscFunctionReturn(0);
9195 }
9196 
9197 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat) {
9198   VecScatter ctx;
9199 
9200   PetscFunctionBegin;
9201   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9202   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9203   PetscCall(VecScatterDestroy(&ctx));
9204   PetscFunctionReturn(0);
9205 }
9206 
9207 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[]) {
9208   const PetscInt Nc = uOff[1] - uOff[0];
9209   PetscInt       c;
9210   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9211 }
9212 
9213 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass) {
9214   DM           dmc;
9215   PetscDS      ds;
9216   Vec          ones, locmass;
9217   IS           cellIS;
9218   PetscFormKey key;
9219   PetscInt     depth;
9220 
9221   PetscFunctionBegin;
9222   PetscCall(DMClone(dm, &dmc));
9223   PetscCall(DMCopyDisc(dm, dmc));
9224   PetscCall(DMGetDS(dmc, &ds));
9225   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9226   PetscCall(DMCreateGlobalVector(dmc, mass));
9227   PetscCall(DMGetLocalVector(dmc, &ones));
9228   PetscCall(DMGetLocalVector(dmc, &locmass));
9229   PetscCall(DMPlexGetDepth(dmc, &depth));
9230   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9231   PetscCall(VecSet(locmass, 0.0));
9232   PetscCall(VecSet(ones, 1.0));
9233   key.label = NULL;
9234   key.value = 0;
9235   key.field = 0;
9236   key.part  = 0;
9237   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9238   PetscCall(ISDestroy(&cellIS));
9239   PetscCall(VecSet(*mass, 0.0));
9240   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9241   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9242   PetscCall(DMRestoreLocalVector(dmc, &ones));
9243   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9244   PetscCall(DMDestroy(&dmc));
9245   PetscFunctionReturn(0);
9246 }
9247 
9248 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass) {
9249   PetscSection gsc, gsf;
9250   PetscInt     m, n;
9251   void        *ctx;
9252   DM           cdm;
9253   PetscBool    regular;
9254 
9255   PetscFunctionBegin;
9256   if (dmFine == dmCoarse) {
9257     DM            dmc;
9258     PetscDS       ds;
9259     PetscWeakForm wf;
9260     Vec           u;
9261     IS            cellIS;
9262     PetscFormKey  key;
9263     PetscInt      depth;
9264 
9265     PetscCall(DMClone(dmFine, &dmc));
9266     PetscCall(DMCopyDisc(dmFine, dmc));
9267     PetscCall(DMGetDS(dmc, &ds));
9268     PetscCall(PetscDSGetWeakForm(ds, &wf));
9269     PetscCall(PetscWeakFormClear(wf));
9270     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9271     PetscCall(DMCreateMatrix(dmc, mass));
9272     PetscCall(DMGetLocalVector(dmc, &u));
9273     PetscCall(DMPlexGetDepth(dmc, &depth));
9274     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9275     PetscCall(MatZeroEntries(*mass));
9276     key.label = NULL;
9277     key.value = 0;
9278     key.field = 0;
9279     key.part  = 0;
9280     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9281     PetscCall(ISDestroy(&cellIS));
9282     PetscCall(DMRestoreLocalVector(dmc, &u));
9283     PetscCall(DMDestroy(&dmc));
9284   } else {
9285     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9286     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9287     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9288     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9289 
9290     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9291     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9292     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9293     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9294 
9295     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9296     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9297     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9298     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9299   }
9300   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9301   PetscFunctionReturn(0);
9302 }
9303 
9304 /*@
9305   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9306 
9307   Input Parameter:
9308 . dm - The DMPlex object
9309 
9310   Output Parameter:
9311 . regular - The flag
9312 
9313   Level: intermediate
9314 
9315 .seealso: `DMPlexSetRegularRefinement()`
9316 @*/
9317 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular) {
9318   PetscFunctionBegin;
9319   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9320   PetscValidBoolPointer(regular, 2);
9321   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9322   PetscFunctionReturn(0);
9323 }
9324 
9325 /*@
9326   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9327 
9328   Input Parameters:
9329 + dm - The DMPlex object
9330 - regular - The flag
9331 
9332   Level: intermediate
9333 
9334 .seealso: `DMPlexGetRegularRefinement()`
9335 @*/
9336 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular) {
9337   PetscFunctionBegin;
9338   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9339   ((DM_Plex *)dm->data)->regularRefinement = regular;
9340   PetscFunctionReturn(0);
9341 }
9342 
9343 /* anchors */
9344 /*@
9345   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9346   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9347 
9348   not collective
9349 
9350   Input Parameter:
9351 . dm - The DMPlex object
9352 
9353   Output Parameters:
9354 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9355 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9356 
9357   Level: intermediate
9358 
9359 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9360 @*/
9361 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS) {
9362   DM_Plex *plex = (DM_Plex *)dm->data;
9363 
9364   PetscFunctionBegin;
9365   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9366   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9367   if (anchorSection) *anchorSection = plex->anchorSection;
9368   if (anchorIS) *anchorIS = plex->anchorIS;
9369   PetscFunctionReturn(0);
9370 }
9371 
9372 /*@
9373   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9374   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9375   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9376 
9377   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9378   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9379 
9380   collective on dm
9381 
9382   Input Parameters:
9383 + dm - The DMPlex object
9384 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9385 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9386 
9387   The reference counts of anchorSection and anchorIS are incremented.
9388 
9389   Level: intermediate
9390 
9391 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9392 @*/
9393 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS) {
9394   DM_Plex    *plex = (DM_Plex *)dm->data;
9395   PetscMPIInt result;
9396 
9397   PetscFunctionBegin;
9398   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9399   if (anchorSection) {
9400     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9401     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9402     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9403   }
9404   if (anchorIS) {
9405     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9406     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9407     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9408   }
9409 
9410   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9411   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9412   plex->anchorSection = anchorSection;
9413 
9414   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9415   PetscCall(ISDestroy(&plex->anchorIS));
9416   plex->anchorIS = anchorIS;
9417 
9418   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9419     PetscInt        size, a, pStart, pEnd;
9420     const PetscInt *anchors;
9421 
9422     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9423     PetscCall(ISGetLocalSize(anchorIS, &size));
9424     PetscCall(ISGetIndices(anchorIS, &anchors));
9425     for (a = 0; a < size; a++) {
9426       PetscInt p;
9427 
9428       p = anchors[a];
9429       if (p >= pStart && p < pEnd) {
9430         PetscInt dof;
9431 
9432         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9433         if (dof) {
9434           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9435           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9436         }
9437       }
9438     }
9439     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9440   }
9441   /* reset the generic constraints */
9442   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9443   PetscFunctionReturn(0);
9444 }
9445 
9446 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec) {
9447   PetscSection anchorSection;
9448   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9449 
9450   PetscFunctionBegin;
9451   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9452   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9453   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9454   PetscCall(PetscSectionGetNumFields(section, &numFields));
9455   if (numFields) {
9456     PetscInt f;
9457     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9458 
9459     for (f = 0; f < numFields; f++) {
9460       PetscInt numComp;
9461 
9462       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9463       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9464     }
9465   }
9466   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9467   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9468   pStart = PetscMax(pStart, sStart);
9469   pEnd   = PetscMin(pEnd, sEnd);
9470   pEnd   = PetscMax(pStart, pEnd);
9471   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9472   for (p = pStart; p < pEnd; p++) {
9473     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9474     if (dof) {
9475       PetscCall(PetscSectionGetDof(section, p, &dof));
9476       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9477       for (f = 0; f < numFields; f++) {
9478         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9479         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9480       }
9481     }
9482   }
9483   PetscCall(PetscSectionSetUp(*cSec));
9484   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9485   PetscFunctionReturn(0);
9486 }
9487 
9488 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat) {
9489   PetscSection    aSec;
9490   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9491   const PetscInt *anchors;
9492   PetscInt        numFields, f;
9493   IS              aIS;
9494   MatType         mtype;
9495   PetscBool       iscuda, iskokkos;
9496 
9497   PetscFunctionBegin;
9498   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9499   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9500   PetscCall(PetscSectionGetStorageSize(section, &n));
9501   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9502   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9503   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9504   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9505   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9506   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9507   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9508   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9509   else mtype = MATSEQAIJ;
9510   PetscCall(MatSetType(*cMat, mtype));
9511   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9512   PetscCall(ISGetIndices(aIS, &anchors));
9513   /* cSec will be a subset of aSec and section */
9514   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9515   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9516   PetscCall(PetscMalloc1(m + 1, &i));
9517   i[0] = 0;
9518   PetscCall(PetscSectionGetNumFields(section, &numFields));
9519   for (p = pStart; p < pEnd; p++) {
9520     PetscInt rDof, rOff, r;
9521 
9522     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9523     if (!rDof) continue;
9524     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9525     if (numFields) {
9526       for (f = 0; f < numFields; f++) {
9527         annz = 0;
9528         for (r = 0; r < rDof; r++) {
9529           a = anchors[rOff + r];
9530           if (a < sStart || a >= sEnd) continue;
9531           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9532           annz += aDof;
9533         }
9534         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9535         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9536         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9537       }
9538     } else {
9539       annz = 0;
9540       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9541       for (q = 0; q < dof; q++) {
9542         a = anchors[rOff + q];
9543         if (a < sStart || a >= sEnd) continue;
9544         PetscCall(PetscSectionGetDof(section, a, &aDof));
9545         annz += aDof;
9546       }
9547       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9548       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9549       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9550     }
9551   }
9552   nnz = i[m];
9553   PetscCall(PetscMalloc1(nnz, &j));
9554   offset = 0;
9555   for (p = pStart; p < pEnd; p++) {
9556     if (numFields) {
9557       for (f = 0; f < numFields; f++) {
9558         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9559         for (q = 0; q < dof; q++) {
9560           PetscInt rDof, rOff, r;
9561           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9562           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9563           for (r = 0; r < rDof; r++) {
9564             PetscInt s;
9565 
9566             a = anchors[rOff + r];
9567             if (a < sStart || a >= sEnd) continue;
9568             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9569             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9570             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9571           }
9572         }
9573       }
9574     } else {
9575       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9576       for (q = 0; q < dof; q++) {
9577         PetscInt rDof, rOff, r;
9578         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9579         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9580         for (r = 0; r < rDof; r++) {
9581           PetscInt s;
9582 
9583           a = anchors[rOff + r];
9584           if (a < sStart || a >= sEnd) continue;
9585           PetscCall(PetscSectionGetDof(section, a, &aDof));
9586           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9587           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9588         }
9589       }
9590     }
9591   }
9592   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9593   PetscCall(PetscFree(i));
9594   PetscCall(PetscFree(j));
9595   PetscCall(ISRestoreIndices(aIS, &anchors));
9596   PetscFunctionReturn(0);
9597 }
9598 
9599 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm) {
9600   DM_Plex     *plex = (DM_Plex *)dm->data;
9601   PetscSection anchorSection, section, cSec;
9602   Mat          cMat;
9603 
9604   PetscFunctionBegin;
9605   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9606   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9607   if (anchorSection) {
9608     PetscInt Nf;
9609 
9610     PetscCall(DMGetLocalSection(dm, &section));
9611     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9612     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9613     PetscCall(DMGetNumFields(dm, &Nf));
9614     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9615     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9616     PetscCall(PetscSectionDestroy(&cSec));
9617     PetscCall(MatDestroy(&cMat));
9618   }
9619   PetscFunctionReturn(0);
9620 }
9621 
9622 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm) {
9623   IS           subis;
9624   PetscSection section, subsection;
9625 
9626   PetscFunctionBegin;
9627   PetscCall(DMGetLocalSection(dm, &section));
9628   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9629   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9630   /* Create subdomain */
9631   PetscCall(DMPlexFilter(dm, label, value, subdm));
9632   /* Create submodel */
9633   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9634   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9635   PetscCall(DMSetLocalSection(*subdm, subsection));
9636   PetscCall(PetscSectionDestroy(&subsection));
9637   PetscCall(DMCopyDisc(dm, *subdm));
9638   /* Create map from submodel to global model */
9639   if (is) {
9640     PetscSection    sectionGlobal, subsectionGlobal;
9641     IS              spIS;
9642     const PetscInt *spmap;
9643     PetscInt       *subIndices;
9644     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9645     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9646 
9647     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9648     PetscCall(ISGetIndices(spIS, &spmap));
9649     PetscCall(PetscSectionGetNumFields(section, &Nf));
9650     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9651     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9652     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9653     for (p = pStart; p < pEnd; ++p) {
9654       PetscInt gdof, pSubSize = 0;
9655 
9656       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9657       if (gdof > 0) {
9658         for (f = 0; f < Nf; ++f) {
9659           PetscInt fdof, fcdof;
9660 
9661           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9662           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9663           pSubSize += fdof - fcdof;
9664         }
9665         subSize += pSubSize;
9666         if (pSubSize) {
9667           if (bs < 0) {
9668             bs = pSubSize;
9669           } else if (bs != pSubSize) {
9670             /* Layout does not admit a pointwise block size */
9671             bs = 1;
9672           }
9673         }
9674       }
9675     }
9676     /* Must have same blocksize on all procs (some might have no points) */
9677     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
9678     bsLocal[1] = bs;
9679     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
9680     if (bsMinMax[0] != bsMinMax[1]) {
9681       bs = 1;
9682     } else {
9683       bs = bsMinMax[0];
9684     }
9685     PetscCall(PetscMalloc1(subSize, &subIndices));
9686     for (p = pStart; p < pEnd; ++p) {
9687       PetscInt gdof, goff;
9688 
9689       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9690       if (gdof > 0) {
9691         const PetscInt point = spmap[p];
9692 
9693         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9694         for (f = 0; f < Nf; ++f) {
9695           PetscInt fdof, fcdof, fc, f2, poff = 0;
9696 
9697           /* Can get rid of this loop by storing field information in the global section */
9698           for (f2 = 0; f2 < f; ++f2) {
9699             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9700             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9701             poff += fdof - fcdof;
9702           }
9703           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9704           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9705           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
9706         }
9707       }
9708     }
9709     PetscCall(ISRestoreIndices(spIS, &spmap));
9710     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9711     if (bs > 1) {
9712       /* We need to check that the block size does not come from non-contiguous fields */
9713       PetscInt i, j, set = 1;
9714       for (i = 0; i < subSize; i += bs) {
9715         for (j = 0; j < bs; ++j) {
9716           if (subIndices[i + j] != subIndices[i] + j) {
9717             set = 0;
9718             break;
9719           }
9720         }
9721       }
9722       if (set) PetscCall(ISSetBlockSize(*is, bs));
9723     }
9724     /* Attach nullspace */
9725     for (f = 0; f < Nf; ++f) {
9726       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9727       if ((*subdm)->nullspaceConstructors[f]) break;
9728     }
9729     if (f < Nf) {
9730       MatNullSpace nullSpace;
9731       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9732 
9733       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
9734       PetscCall(MatNullSpaceDestroy(&nullSpace));
9735     }
9736   }
9737   PetscFunctionReturn(0);
9738 }
9739 
9740 /*@
9741   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9742 
9743   Input Parameter:
9744 - dm - The DM
9745 
9746   Level: developer
9747 
9748   Options Database Keys:
9749 . -dm_plex_monitor_throughput - Activate the monitor
9750 
9751 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9752 @*/
9753 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy) {
9754 #if defined(PETSC_USE_LOG)
9755   PetscStageLog      stageLog;
9756   PetscLogEvent      event;
9757   PetscLogStage      stage;
9758   PetscEventPerfInfo eventInfo;
9759   PetscReal          cellRate, flopRate;
9760   PetscInt           cStart, cEnd, Nf, N;
9761   const char        *name;
9762 #endif
9763 
9764   PetscFunctionBegin;
9765   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9766 #if defined(PETSC_USE_LOG)
9767   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
9768   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9769   PetscCall(DMGetNumFields(dm, &Nf));
9770   PetscCall(PetscLogGetStageLog(&stageLog));
9771   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9772   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9773   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9774   N        = (cEnd - cStart) * Nf * eventInfo.count;
9775   flopRate = eventInfo.flops / eventInfo.time;
9776   cellRate = N / eventInfo.time;
9777   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)));
9778 #else
9779   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9780 #endif
9781   PetscFunctionReturn(0);
9782 }
9783