xref: /petsc/src/dm/impls/plex/plex.c (revision a03d55ff11d5b27b1a9323bff838ec4fc2621dcb)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The `DMPLEX` object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(PETSC_SUCCESS);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(PETSC_SUCCESS);
49 }
50 
51 /*@
52   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
53 
54   Input Parameters:
55 + dm     - The `DMPLEX` object
56 - height - The cell height in the Plex, 0 is the default
57 
58   Output Parameters:
59 + cStart - The first "normal" cell
60 - cEnd   - The upper bound on "normal"" cells
61 
62   Level: developer
63 
64   Note:
65   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
66 
67 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(PETSC_SUCCESS);
106 }
107 
108 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
109 {
110   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
111   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
112 
113   PetscFunctionBegin;
114   *ft = PETSC_VTK_INVALID;
115   PetscCall(DMGetCoordinateDim(dm, &cdim));
116   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
118   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
119   if (field >= 0) {
120     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
121     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
122   } else {
123     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
124     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
125   }
126   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(PETSC_SUCCESS);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective
154 
155   Input Parameters:
156 + dm - The `DMPLEX` object
157 . n  - The number of vectors
158 . u  - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
202         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
203         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(PETSC_SUCCESS);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *array;
253   PetscReal          lbound[3], ubound[3];
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
278   PetscCall(PetscDrawClear(draw));
279 
280   /* Could implement something like DMDASelectFields() */
281   for (f = 0; f < Nf; ++f) {
282     DM          fdm = dm;
283     Vec         fv  = v;
284     IS          fis;
285     char        prefix[PETSC_MAX_PATH_LEN];
286     const char *fname;
287 
288     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
289     PetscCall(PetscSectionGetFieldName(s, f, &fname));
290 
291     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
292     else prefix[0] = '\0';
293     if (Nf > 1) {
294       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
295       PetscCall(VecGetSubVector(v, fis, &fv));
296       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
297       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
298     }
299     for (comp = 0; comp < Nc; ++comp, ++w) {
300       PetscInt nmax = 2;
301 
302       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
303       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
304       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
305       PetscCall(PetscDrawSetTitle(draw, title));
306 
307       /* TODO Get max and min only for this component */
308       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
309       if (!flg) {
310         PetscCall(VecMin(fv, NULL, &vbound[0]));
311         PetscCall(VecMax(fv, NULL, &vbound[1]));
312         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
313       }
314 
315       PetscCall(PetscDrawGetPopup(draw, &popup));
316       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
317       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
318       PetscCall(VecGetArrayRead(fv, &array));
319       for (c = cStart; c < cEnd; ++c) {
320         PetscScalar       *coords = NULL, *a = NULL;
321         const PetscScalar *coords_arr;
322         PetscBool          isDG;
323         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
324 
325         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
326         if (a) {
327           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
328           color[1] = color[2] = color[3] = color[0];
329         } else {
330           PetscScalar *vals = NULL;
331           PetscInt     numVals, va;
332 
333           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
334           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
335           switch (numVals / Nc) {
336           case 3: /* P1 Triangle */
337           case 4: /* P1 Quadrangle */
338             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
339             break;
340           case 6: /* P2 Triangle */
341           case 8: /* P2 Quadrangle */
342             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
343             break;
344           default:
345             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
346           }
347           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
348         }
349         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
350         switch (numCoords) {
351         case 6:
352         case 12: /* Localized triangle */
353           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
354           break;
355         case 8:
356         case 16: /* Localized quadrilateral */
357           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
358           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
359           break;
360         default:
361           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
362         }
363         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
364       }
365       PetscCall(VecRestoreArrayRead(fv, &array));
366       PetscCall(PetscDrawFlush(draw));
367       PetscCall(PetscDrawPause(draw));
368       PetscCall(PetscDrawSave(draw));
369     }
370     if (Nf > 1) {
371       PetscCall(VecRestoreSubVector(v, fis, &fv));
372       PetscCall(ISDestroy(&fis));
373       PetscCall(DMDestroy(&fdm));
374     }
375   }
376   PetscFunctionReturn(PETSC_SUCCESS);
377 }
378 
379 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
380 {
381   DM        dm;
382   PetscDraw draw;
383   PetscInt  dim;
384   PetscBool isnull;
385 
386   PetscFunctionBegin;
387   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
388   PetscCall(PetscDrawIsNull(draw, &isnull));
389   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
390 
391   PetscCall(VecGetDM(v, &dm));
392   PetscCall(DMGetCoordinateDim(dm, &dim));
393   switch (dim) {
394   case 1:
395     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
396     break;
397   case 2:
398     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
399     break;
400   default:
401     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
402   }
403   PetscFunctionReturn(PETSC_SUCCESS);
404 }
405 
406 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
407 {
408   DM                      dm;
409   Vec                     locv;
410   const char             *name;
411   PetscSection            section;
412   PetscInt                pStart, pEnd;
413   PetscInt                numFields;
414   PetscViewerVTKFieldType ft;
415 
416   PetscFunctionBegin;
417   PetscCall(VecGetDM(v, &dm));
418   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
419   PetscCall(PetscObjectGetName((PetscObject)v, &name));
420   PetscCall(PetscObjectSetName((PetscObject)locv, name));
421   PetscCall(VecCopy(v, locv));
422   PetscCall(DMGetLocalSection(dm, &section));
423   PetscCall(PetscSectionGetNumFields(section, &numFields));
424   if (!numFields) {
425     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
426     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
427   } else {
428     PetscInt f;
429 
430     for (f = 0; f < numFields; f++) {
431       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
432       if (ft == PETSC_VTK_INVALID) continue;
433       PetscCall(PetscObjectReference((PetscObject)locv));
434       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
435     }
436     PetscCall(VecDestroy(&locv));
437   }
438   PetscFunctionReturn(PETSC_SUCCESS);
439 }
440 
441 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
442 {
443   DM        dm;
444   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
445 
446   PetscFunctionBegin;
447   PetscCall(VecGetDM(v, &dm));
448   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
449   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
450   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
453   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
454   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
455     PetscInt    i, numFields;
456     PetscObject fe;
457     PetscBool   fem  = PETSC_FALSE;
458     Vec         locv = v;
459     const char *name;
460     PetscInt    step;
461     PetscReal   time;
462 
463     PetscCall(DMGetNumFields(dm, &numFields));
464     for (i = 0; i < numFields; i++) {
465       PetscCall(DMGetField(dm, i, NULL, &fe));
466       if (fe->classid == PETSCFE_CLASSID) {
467         fem = PETSC_TRUE;
468         break;
469       }
470     }
471     if (fem) {
472       PetscObject isZero;
473 
474       PetscCall(DMGetLocalVector(dm, &locv));
475       PetscCall(PetscObjectGetName((PetscObject)v, &name));
476       PetscCall(PetscObjectSetName((PetscObject)locv, name));
477       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
478       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
479       PetscCall(VecCopy(v, locv));
480       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
481       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
482     }
483     if (isvtk) {
484       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
485     } else if (ishdf5) {
486 #if defined(PETSC_HAVE_HDF5)
487       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
488 #else
489       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
490 #endif
491     } else if (isdraw) {
492       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
493     } else if (isglvis) {
494       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
495       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
496       PetscCall(VecView_GLVis(locv, viewer));
497     } else if (iscgns) {
498 #if defined(PETSC_HAVE_CGNS)
499       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
500 #else
501       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
502 #endif
503     }
504     if (fem) {
505       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
506       PetscCall(DMRestoreLocalVector(dm, &locv));
507     }
508   } else {
509     PetscBool isseq;
510 
511     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
512     if (isseq) PetscCall(VecView_Seq(v, viewer));
513     else PetscCall(VecView_MPI(v, viewer));
514   }
515   PetscFunctionReturn(PETSC_SUCCESS);
516 }
517 
518 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
519 {
520   DM        dm;
521   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
527   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
528   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
529   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
532   if (isvtk || isdraw || isglvis || iscgns) {
533     Vec         locv;
534     PetscObject isZero;
535     const char *name;
536 
537     PetscCall(DMGetLocalVector(dm, &locv));
538     PetscCall(PetscObjectGetName((PetscObject)v, &name));
539     PetscCall(PetscObjectSetName((PetscObject)locv, name));
540     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
541     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
542     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
543     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
544     PetscCall(VecView_Plex_Local(locv, viewer));
545     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
546     PetscCall(DMRestoreLocalVector(dm, &locv));
547   } else if (ishdf5) {
548 #if defined(PETSC_HAVE_HDF5)
549     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
550 #else
551     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
552 #endif
553   } else if (isexodusii) {
554 #if defined(PETSC_HAVE_EXODUSII)
555     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
556 #else
557     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
558 #endif
559   } else {
560     PetscBool isseq;
561 
562     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
563     if (isseq) PetscCall(VecView_Seq(v, viewer));
564     else PetscCall(VecView_MPI(v, viewer));
565   }
566   PetscFunctionReturn(PETSC_SUCCESS);
567 }
568 
569 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
570 {
571   DM                dm;
572   MPI_Comm          comm;
573   PetscViewerFormat format;
574   Vec               v;
575   PetscBool         isvtk, ishdf5;
576 
577   PetscFunctionBegin;
578   PetscCall(VecGetDM(originalv, &dm));
579   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
580   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
581   PetscCall(PetscViewerGetFormat(viewer, &format));
582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
583   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
584   if (format == PETSC_VIEWER_NATIVE) {
585     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
586     /* this need a better fix */
587     if (dm->useNatural) {
588       if (dm->sfNatural) {
589         const char *vecname;
590         PetscInt    n, nroots;
591 
592         PetscCall(VecGetLocalSize(originalv, &n));
593         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
594         if (n == nroots) {
595           PetscCall(DMPlexCreateNaturalVector(dm, &v));
596           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
597           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
598           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
599           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
600         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
601       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
602     } else v = originalv;
603   } else v = originalv;
604 
605   if (ishdf5) {
606 #if defined(PETSC_HAVE_HDF5)
607     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
608 #else
609     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
610 #endif
611   } else if (isvtk) {
612     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
613   } else {
614     PetscBool isseq;
615 
616     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
617     if (isseq) PetscCall(VecView_Seq(v, viewer));
618     else PetscCall(VecView_MPI(v, viewer));
619   }
620   if (v != originalv) PetscCall(VecDestroy(&v));
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
625 {
626   DM        dm;
627   PetscBool ishdf5;
628 
629   PetscFunctionBegin;
630   PetscCall(VecGetDM(v, &dm));
631   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
633   if (ishdf5) {
634     DM          dmBC;
635     Vec         gv;
636     const char *name;
637 
638     PetscCall(DMGetOutputDM(dm, &dmBC));
639     PetscCall(DMGetGlobalVector(dmBC, &gv));
640     PetscCall(PetscObjectGetName((PetscObject)v, &name));
641     PetscCall(PetscObjectSetName((PetscObject)gv, name));
642     PetscCall(VecLoad_Default(gv, viewer));
643     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
644     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
645     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
646   } else PetscCall(VecLoad_Default(v, viewer));
647   PetscFunctionReturn(PETSC_SUCCESS);
648 }
649 
650 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
651 {
652   DM        dm;
653   PetscBool ishdf5, isexodusii;
654 
655   PetscFunctionBegin;
656   PetscCall(VecGetDM(v, &dm));
657   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
659   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
660   if (ishdf5) {
661 #if defined(PETSC_HAVE_HDF5)
662     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
665 #endif
666   } else if (isexodusii) {
667 #if defined(PETSC_HAVE_EXODUSII)
668     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
669 #else
670     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
671 #endif
672   } else PetscCall(VecLoad_Default(v, viewer));
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   PetscViewerFormat format;
680   PetscBool         ishdf5;
681 
682   PetscFunctionBegin;
683   PetscCall(VecGetDM(originalv, &dm));
684   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
685   PetscCall(PetscViewerGetFormat(viewer, &format));
686   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
687   if (format == PETSC_VIEWER_NATIVE) {
688     if (dm->useNatural) {
689       if (dm->sfNatural) {
690         if (ishdf5) {
691 #if defined(PETSC_HAVE_HDF5)
692           Vec         v;
693           const char *vecname;
694 
695           PetscCall(DMPlexCreateNaturalVector(dm, &v));
696           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
697           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
698           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
699           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
700           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
701           PetscCall(VecDestroy(&v));
702 #else
703           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
704 #endif
705         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
706       }
707     } else PetscCall(VecLoad_Default(originalv, viewer));
708   }
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
713 {
714   PetscSection       coordSection;
715   Vec                coordinates;
716   DMLabel            depthLabel, celltypeLabel;
717   const char        *name[4];
718   const PetscScalar *a;
719   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
720 
721   PetscFunctionBegin;
722   PetscCall(DMGetDimension(dm, &dim));
723   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
724   PetscCall(DMGetCoordinateSection(dm, &coordSection));
725   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
726   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
727   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
728   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
729   PetscCall(VecGetArrayRead(coordinates, &a));
730   name[0]       = "vertex";
731   name[1]       = "edge";
732   name[dim - 1] = "face";
733   name[dim]     = "cell";
734   for (c = cStart; c < cEnd; ++c) {
735     PetscInt *closure = NULL;
736     PetscInt  closureSize, cl, ct;
737 
738     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
739     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
740     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
741     PetscCall(PetscViewerASCIIPushTab(viewer));
742     for (cl = 0; cl < closureSize * 2; cl += 2) {
743       PetscInt point = closure[cl], depth, dof, off, d, p;
744 
745       if ((point < pStart) || (point >= pEnd)) continue;
746       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
747       if (!dof) continue;
748       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
749       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
750       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
751       for (p = 0; p < dof / dim; ++p) {
752         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
753         for (d = 0; d < dim; ++d) {
754           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
755           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
756         }
757         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
758       }
759       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
760     }
761     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
762     PetscCall(PetscViewerASCIIPopTab(viewer));
763   }
764   PetscCall(VecRestoreArrayRead(coordinates, &a));
765   PetscFunctionReturn(PETSC_SUCCESS);
766 }
767 
768 typedef enum {
769   CS_CARTESIAN,
770   CS_POLAR,
771   CS_CYLINDRICAL,
772   CS_SPHERICAL
773 } CoordSystem;
774 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
775 
776 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
777 {
778   PetscInt i;
779 
780   PetscFunctionBegin;
781   if (dim > 3) {
782     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
783   } else {
784     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
785 
786     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
787     switch (cs) {
788     case CS_CARTESIAN:
789       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
790       break;
791     case CS_POLAR:
792       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
793       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
794       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
795       break;
796     case CS_CYLINDRICAL:
797       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       trcoords[2] = coords[2];
801       break;
802     case CS_SPHERICAL:
803       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
804       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
805       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
806       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
807       break;
808     }
809     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
810   }
811   PetscFunctionReturn(PETSC_SUCCESS);
812 }
813 
814 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
815 {
816   DM_Plex          *mesh = (DM_Plex *)dm->data;
817   DM                cdm, cdmCell;
818   PetscSection      coordSection, coordSectionCell;
819   Vec               coordinates, coordinatesCell;
820   PetscViewerFormat format;
821 
822   PetscFunctionBegin;
823   PetscCall(PetscViewerGetFormat(viewer, &format));
824   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
825     const char *name;
826     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
827     PetscInt    pStart, pEnd, p, numLabels, l;
828     PetscMPIInt rank, size;
829 
830     PetscCall(DMGetCoordinateDM(dm, &cdm));
831     PetscCall(DMGetCoordinateSection(dm, &coordSection));
832     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
833     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
834     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
835     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
836     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
837     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
838     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
839     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
840     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
841     PetscCall(DMGetDimension(dm, &dim));
842     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
843     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
844     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
845     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
847     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
848     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
849     for (p = pStart; p < pEnd; ++p) {
850       PetscInt dof, off, s;
851 
852       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
853       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
854       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
855     }
856     PetscCall(PetscViewerFlush(viewer));
857     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
858     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
859     for (p = pStart; p < pEnd; ++p) {
860       PetscInt dof, off, c;
861 
862       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
863       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
864       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
865     }
866     PetscCall(PetscViewerFlush(viewer));
867     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
868     if (coordSection && coordinates) {
869       CoordSystem        cs = CS_CARTESIAN;
870       const PetscScalar *array, *arrayCell = NULL;
871       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
872       PetscMPIInt        rank;
873       const char        *name;
874 
875       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
876       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
877       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
878       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
879       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
880       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
881       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
882       pStart = PetscMin(pvStart, pcStart);
883       pEnd   = PetscMax(pvEnd, pcEnd);
884       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
885       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
886       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
887       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
888 
889       PetscCall(VecGetArrayRead(coordinates, &array));
890       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
891       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
892       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
893       for (p = pStart; p < pEnd; ++p) {
894         PetscInt dof, off;
895 
896         if (p >= pvStart && p < pvEnd) {
897           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
898           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
899           if (dof) {
900             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
901             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
902             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
903           }
904         }
905         if (cdmCell && p >= pcStart && p < pcEnd) {
906           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
907           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
908           if (dof) {
909             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
910             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
911             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
912           }
913         }
914       }
915       PetscCall(PetscViewerFlush(viewer));
916       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
917       PetscCall(VecRestoreArrayRead(coordinates, &array));
918       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
919     }
920     PetscCall(DMGetNumLabels(dm, &numLabels));
921     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
922     for (l = 0; l < numLabels; ++l) {
923       DMLabel     label;
924       PetscBool   isdepth;
925       const char *name;
926 
927       PetscCall(DMGetLabelName(dm, l, &name));
928       PetscCall(PetscStrcmp(name, "depth", &isdepth));
929       if (isdepth) continue;
930       PetscCall(DMGetLabel(dm, name, &label));
931       PetscCall(DMLabelView(label, viewer));
932     }
933     if (size > 1) {
934       PetscSF sf;
935 
936       PetscCall(DMGetPointSF(dm, &sf));
937       PetscCall(PetscSFView(sf, viewer));
938     }
939     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
940     PetscCall(PetscViewerFlush(viewer));
941   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
942     const char  *name, *color;
943     const char  *defcolors[3]  = {"gray", "orange", "green"};
944     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
945     char         lname[PETSC_MAX_PATH_LEN];
946     PetscReal    scale      = 2.0;
947     PetscReal    tikzscale  = 1.0;
948     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
949     double       tcoords[3];
950     PetscScalar *coords;
951     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
952     PetscMPIInt  rank, size;
953     char       **names, **colors, **lcolors;
954     PetscBool    flg, lflg;
955     PetscBT      wp = NULL;
956     PetscInt     pEnd, pStart;
957 
958     PetscCall(DMGetCoordinateDM(dm, &cdm));
959     PetscCall(DMGetCoordinateSection(dm, &coordSection));
960     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
961     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
962     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
963     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
964     PetscCall(DMGetDimension(dm, &dim));
965     PetscCall(DMPlexGetDepth(dm, &depth));
966     PetscCall(DMGetNumLabels(dm, &numLabels));
967     numLabels  = PetscMax(numLabels, 10);
968     numColors  = 10;
969     numLColors = 10;
970     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
971     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
972     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
973     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
974     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
975     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
976     n = 4;
977     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
978     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
979     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
980     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
981     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
982     if (!useLabels) numLabels = 0;
983     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
984     if (!useColors) {
985       numColors = 3;
986       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
987     }
988     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
989     if (!useColors) {
990       numLColors = 4;
991       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
992     }
993     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
994     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
995     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
996     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
997     if (depth < dim) plotEdges = PETSC_FALSE;
998     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
999 
1000     /* filter points with labelvalue != labeldefaultvalue */
1001     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1002     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1003     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1004     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1005     if (lflg) {
1006       DMLabel lbl;
1007 
1008       PetscCall(DMGetLabel(dm, lname, &lbl));
1009       if (lbl) {
1010         PetscInt val, defval;
1011 
1012         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1013         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1014         for (c = pStart; c < pEnd; c++) {
1015           PetscInt *closure = NULL;
1016           PetscInt  closureSize;
1017 
1018           PetscCall(DMLabelGetValue(lbl, c, &val));
1019           if (val == defval) continue;
1020 
1021           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1022           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1023           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1024         }
1025       }
1026     }
1027 
1028     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1029     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1030     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1031     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1032 \\documentclass[tikz]{standalone}\n\n\
1033 \\usepackage{pgflibraryshapes}\n\
1034 \\usetikzlibrary{backgrounds}\n\
1035 \\usetikzlibrary{arrows}\n\
1036 \\begin{document}\n"));
1037     if (size > 1) {
1038       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1039       for (p = 0; p < size; ++p) {
1040         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1041         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1042       }
1043       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1044     }
1045     if (drawHasse) {
1046       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1047 
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1060     }
1061     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1062 
1063     /* Plot vertices */
1064     PetscCall(VecGetArray(coordinates, &coords));
1065     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1066     for (v = vStart; v < vEnd; ++v) {
1067       PetscInt  off, dof, d;
1068       PetscBool isLabeled = PETSC_FALSE;
1069 
1070       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1071       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1072       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1073       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1074       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1075       for (d = 0; d < dof; ++d) {
1076         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1077         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1078       }
1079       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1080       if (dim == 3) {
1081         PetscReal tmp = tcoords[1];
1082         tcoords[1]    = tcoords[2];
1083         tcoords[2]    = -tmp;
1084       }
1085       for (d = 0; d < dof; ++d) {
1086         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1088       }
1089       if (drawHasse) color = colors[0 % numColors];
1090       else color = colors[rank % numColors];
1091       for (l = 0; l < numLabels; ++l) {
1092         PetscInt val;
1093         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1094         if (val >= 0) {
1095           color     = lcolors[l % numLColors];
1096           isLabeled = PETSC_TRUE;
1097           break;
1098         }
1099       }
1100       if (drawNumbers[0]) {
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1102       } else if (drawColors[0]) {
1103         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1104       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1105     }
1106     PetscCall(VecRestoreArray(coordinates, &coords));
1107     PetscCall(PetscViewerFlush(viewer));
1108     /* Plot edges */
1109     if (plotEdges) {
1110       PetscCall(VecGetArray(coordinates, &coords));
1111       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1112       for (e = eStart; e < eEnd; ++e) {
1113         const PetscInt *cone;
1114         PetscInt        coneSize, offA, offB, dof, d;
1115 
1116         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1117         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1118         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1119         PetscCall(DMPlexGetCone(dm, e, &cone));
1120         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1121         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1122         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1123         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1124         for (d = 0; d < dof; ++d) {
1125           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1126           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1127         }
1128         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1129         if (dim == 3) {
1130           PetscReal tmp = tcoords[1];
1131           tcoords[1]    = tcoords[2];
1132           tcoords[2]    = -tmp;
1133         }
1134         for (d = 0; d < dof; ++d) {
1135           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1136           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1137         }
1138         if (drawHasse) color = colors[1 % numColors];
1139         else color = colors[rank % numColors];
1140         for (l = 0; l < numLabels; ++l) {
1141           PetscInt val;
1142           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1143           if (val >= 0) {
1144             color = lcolors[l % numLColors];
1145             break;
1146           }
1147         }
1148         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1149       }
1150       PetscCall(VecRestoreArray(coordinates, &coords));
1151       PetscCall(PetscViewerFlush(viewer));
1152       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1153     }
1154     /* Plot cells */
1155     if (dim == 3 || !drawNumbers[1]) {
1156       for (e = eStart; e < eEnd; ++e) {
1157         const PetscInt *cone;
1158 
1159         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1160         color = colors[rank % numColors];
1161         for (l = 0; l < numLabels; ++l) {
1162           PetscInt val;
1163           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1164           if (val >= 0) {
1165             color = lcolors[l % numLColors];
1166             break;
1167           }
1168         }
1169         PetscCall(DMPlexGetCone(dm, e, &cone));
1170         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1171       }
1172     } else {
1173       DMPolytopeType ct;
1174 
1175       /* Drawing a 2D polygon */
1176       for (c = cStart; c < cEnd; ++c) {
1177         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1178         PetscCall(DMPlexGetCellType(dm, c, &ct));
1179         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1180           const PetscInt *cone;
1181           PetscInt        coneSize, e;
1182 
1183           PetscCall(DMPlexGetCone(dm, c, &cone));
1184           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1185           for (e = 0; e < coneSize; ++e) {
1186             const PetscInt *econe;
1187 
1188             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1189             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1190           }
1191         } else {
1192           PetscInt *closure = NULL;
1193           PetscInt  closureSize, Nv = 0, v;
1194 
1195           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1196           for (p = 0; p < closureSize * 2; p += 2) {
1197             const PetscInt point = closure[p];
1198 
1199             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1200           }
1201           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1202           for (v = 0; v <= Nv; ++v) {
1203             const PetscInt vertex = closure[v % Nv];
1204 
1205             if (v > 0) {
1206               if (plotEdges) {
1207                 const PetscInt *edge;
1208                 PetscInt        endpoints[2], ne;
1209 
1210                 endpoints[0] = closure[v - 1];
1211                 endpoints[1] = vertex;
1212                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1213                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1214                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1215                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1216               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1217             }
1218             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1219           }
1220           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1221           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1222         }
1223       }
1224     }
1225     for (c = cStart; c < cEnd; ++c) {
1226       double             ccoords[3] = {0.0, 0.0, 0.0};
1227       PetscBool          isLabeled  = PETSC_FALSE;
1228       PetscScalar       *cellCoords = NULL;
1229       const PetscScalar *array;
1230       PetscInt           numCoords, cdim, d;
1231       PetscBool          isDG;
1232 
1233       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1234       PetscCall(DMGetCoordinateDim(dm, &cdim));
1235       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1236       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1237       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1238       for (p = 0; p < numCoords / cdim; ++p) {
1239         for (d = 0; d < cdim; ++d) {
1240           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1241           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1242         }
1243         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1244         if (cdim == 3) {
1245           PetscReal tmp = tcoords[1];
1246           tcoords[1]    = tcoords[2];
1247           tcoords[2]    = -tmp;
1248         }
1249         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1250       }
1251       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1252       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1253       for (d = 0; d < cdim; ++d) {
1254         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1255         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1256       }
1257       if (drawHasse) color = colors[depth % numColors];
1258       else color = colors[rank % numColors];
1259       for (l = 0; l < numLabels; ++l) {
1260         PetscInt val;
1261         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1262         if (val >= 0) {
1263           color     = lcolors[l % numLColors];
1264           isLabeled = PETSC_TRUE;
1265           break;
1266         }
1267       }
1268       if (drawNumbers[dim]) {
1269         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1270       } else if (drawColors[dim]) {
1271         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1272       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1273     }
1274     if (drawHasse) {
1275       color = colors[depth % numColors];
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1277       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1279       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1281 
1282       color = colors[1 % numColors];
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1286       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1288 
1289       color = colors[0 % numColors];
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1295 
1296       for (p = pStart; p < pEnd; ++p) {
1297         const PetscInt *cone;
1298         PetscInt        coneSize, cp;
1299 
1300         PetscCall(DMPlexGetCone(dm, p, &cone));
1301         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1302         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1303       }
1304     }
1305     PetscCall(PetscViewerFlush(viewer));
1306     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1307     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1308     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1309     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1310     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1311     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1312     PetscCall(PetscFree3(names, colors, lcolors));
1313     PetscCall(PetscBTDestroy(&wp));
1314   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1315     Vec                    cown, acown;
1316     VecScatter             sct;
1317     ISLocalToGlobalMapping g2l;
1318     IS                     gid, acis;
1319     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1320     MPI_Group              ggroup, ngroup;
1321     PetscScalar           *array, nid;
1322     const PetscInt        *idxs;
1323     PetscInt              *idxs2, *start, *adjacency, *work;
1324     PetscInt64             lm[3], gm[3];
1325     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1326     PetscMPIInt            d1, d2, rank;
1327 
1328     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1329     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1330 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1331     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1332 #endif
1333     if (ncomm != MPI_COMM_NULL) {
1334       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1335       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1336       d1 = 0;
1337       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1338       nid = d2;
1339       PetscCallMPI(MPI_Group_free(&ggroup));
1340       PetscCallMPI(MPI_Group_free(&ngroup));
1341       PetscCallMPI(MPI_Comm_free(&ncomm));
1342     } else nid = 0.0;
1343 
1344     /* Get connectivity */
1345     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1346     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1347 
1348     /* filter overlapped local cells */
1349     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1350     PetscCall(ISGetIndices(gid, &idxs));
1351     PetscCall(ISGetLocalSize(gid, &cum));
1352     PetscCall(PetscMalloc1(cum, &idxs2));
1353     for (c = cStart, cum = 0; c < cEnd; c++) {
1354       if (idxs[c - cStart] < 0) continue;
1355       idxs2[cum++] = idxs[c - cStart];
1356     }
1357     PetscCall(ISRestoreIndices(gid, &idxs));
1358     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1359     PetscCall(ISDestroy(&gid));
1360     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1361 
1362     /* support for node-aware cell locality */
1363     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1364     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1365     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1366     PetscCall(VecGetArray(cown, &array));
1367     for (c = 0; c < numVertices; c++) array[c] = nid;
1368     PetscCall(VecRestoreArray(cown, &array));
1369     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1370     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1371     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1372     PetscCall(ISDestroy(&acis));
1373     PetscCall(VecScatterDestroy(&sct));
1374     PetscCall(VecDestroy(&cown));
1375 
1376     /* compute edgeCut */
1377     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1378     PetscCall(PetscMalloc1(cum, &work));
1379     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1380     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1381     PetscCall(ISDestroy(&gid));
1382     PetscCall(VecGetArray(acown, &array));
1383     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1384       PetscInt totl;
1385 
1386       totl = start[c + 1] - start[c];
1387       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1388       for (i = 0; i < totl; i++) {
1389         if (work[i] < 0) {
1390           ect += 1;
1391           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1392         }
1393       }
1394     }
1395     PetscCall(PetscFree(work));
1396     PetscCall(VecRestoreArray(acown, &array));
1397     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1398     lm[1] = -numVertices;
1399     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1400     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1401     lm[0] = ect;                     /* edgeCut */
1402     lm[1] = ectn;                    /* node-aware edgeCut */
1403     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1404     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1405     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1406 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1407     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1408 #else
1409     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1410 #endif
1411     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1412     PetscCall(PetscFree(start));
1413     PetscCall(PetscFree(adjacency));
1414     PetscCall(VecDestroy(&acown));
1415   } else {
1416     const char    *name;
1417     PetscInt      *sizes, *hybsizes, *ghostsizes;
1418     PetscInt       locDepth, depth, cellHeight, dim, d;
1419     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1420     PetscInt       numLabels, l, maxSize = 17;
1421     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1422     MPI_Comm       comm;
1423     PetscMPIInt    size, rank;
1424 
1425     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1426     PetscCallMPI(MPI_Comm_size(comm, &size));
1427     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1428     PetscCall(DMGetDimension(dm, &dim));
1429     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1430     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1431     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1432     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1433     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1434     PetscCall(DMPlexGetDepth(dm, &locDepth));
1435     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1436     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1437     gcNum = gcEnd - gcStart;
1438     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1439     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1440     for (d = 0; d <= depth; d++) {
1441       PetscInt Nc[2] = {0, 0}, ict;
1442 
1443       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1444       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1445       ict = ct0;
1446       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1447       ct0 = (DMPolytopeType)ict;
1448       for (p = pStart; p < pEnd; ++p) {
1449         DMPolytopeType ct;
1450 
1451         PetscCall(DMPlexGetCellType(dm, p, &ct));
1452         if (ct == ct0) ++Nc[0];
1453         else ++Nc[1];
1454       }
1455       if (size < maxSize) {
1456         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1457         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1458         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1459         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1460         for (p = 0; p < size; ++p) {
1461           if (rank == 0) {
1462             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1463             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1464             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1465           }
1466         }
1467       } else {
1468         PetscInt locMinMax[2];
1469 
1470         locMinMax[0] = Nc[0] + Nc[1];
1471         locMinMax[1] = Nc[0] + Nc[1];
1472         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1473         locMinMax[0] = Nc[1];
1474         locMinMax[1] = Nc[1];
1475         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1476         if (d == depth) {
1477           locMinMax[0] = gcNum;
1478           locMinMax[1] = gcNum;
1479           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1480         }
1481         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1482         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1483         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1484         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1485       }
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1487     }
1488     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1489     {
1490       const PetscReal *maxCell;
1491       const PetscReal *L;
1492       PetscBool        localized;
1493 
1494       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1495       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1496       if (L || localized) {
1497         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1498         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1499         if (L) {
1500           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1501           for (d = 0; d < dim; ++d) {
1502             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1503             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1504           }
1505           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1506         }
1507         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1508         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1509       }
1510     }
1511     PetscCall(DMGetNumLabels(dm, &numLabels));
1512     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1513     for (l = 0; l < numLabels; ++l) {
1514       DMLabel         label;
1515       const char     *name;
1516       IS              valueIS;
1517       const PetscInt *values;
1518       PetscInt        numValues, v;
1519 
1520       PetscCall(DMGetLabelName(dm, l, &name));
1521       PetscCall(DMGetLabel(dm, name, &label));
1522       PetscCall(DMLabelGetNumValues(label, &numValues));
1523       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1524       PetscCall(DMLabelGetValueIS(label, &valueIS));
1525       PetscCall(ISGetIndices(valueIS, &values));
1526       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1527       for (v = 0; v < numValues; ++v) {
1528         PetscInt size;
1529 
1530         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1531         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1532         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1533       }
1534       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1535       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1536       PetscCall(ISRestoreIndices(valueIS, &values));
1537       PetscCall(ISDestroy(&valueIS));
1538     }
1539     {
1540       char    **labelNames;
1541       PetscInt  Nl = numLabels;
1542       PetscBool flg;
1543 
1544       PetscCall(PetscMalloc1(Nl, &labelNames));
1545       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1546       for (l = 0; l < Nl; ++l) {
1547         DMLabel label;
1548 
1549         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1550         if (flg) {
1551           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1552           PetscCall(DMLabelView(label, viewer));
1553         }
1554         PetscCall(PetscFree(labelNames[l]));
1555       }
1556       PetscCall(PetscFree(labelNames));
1557     }
1558     /* If no fields are specified, people do not want to see adjacency */
1559     if (dm->Nf) {
1560       PetscInt f;
1561 
1562       for (f = 0; f < dm->Nf; ++f) {
1563         const char *name;
1564 
1565         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1566         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1567         PetscCall(PetscViewerASCIIPushTab(viewer));
1568         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1569         if (dm->fields[f].adjacency[0]) {
1570           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1571           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1572         } else {
1573           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1574           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1575         }
1576         PetscCall(PetscViewerASCIIPopTab(viewer));
1577       }
1578     }
1579     PetscCall(DMGetCoarseDM(dm, &cdm));
1580     if (cdm) {
1581       PetscCall(PetscViewerASCIIPushTab(viewer));
1582       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1583       PetscCall(DMPlexView_Ascii(cdm, viewer));
1584       PetscCall(PetscViewerASCIIPopTab(viewer));
1585     }
1586   }
1587   PetscFunctionReturn(PETSC_SUCCESS);
1588 }
1589 
1590 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1591 {
1592   DMPolytopeType ct;
1593   PetscMPIInt    rank;
1594   PetscInt       cdim;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   PetscCall(DMGetCoordinateDim(dm, &cdim));
1600   switch (ct) {
1601   case DM_POLYTOPE_SEGMENT:
1602   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1603     switch (cdim) {
1604     case 1: {
1605       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1606       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1607 
1608       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1609       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1610       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1611     } break;
1612     case 2: {
1613       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1614       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1615       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1616 
1617       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1618       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1619       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1620     } break;
1621     default:
1622       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1623     }
1624     break;
1625   case DM_POLYTOPE_TRIANGLE:
1626     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1627     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1628     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1629     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1630     break;
1631   case DM_POLYTOPE_QUADRILATERAL:
1632     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1633     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1635     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1636     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1637     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1638     break;
1639   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1640     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1641     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1642     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1643     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1644     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1645     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1646     break;
1647   case DM_POLYTOPE_FV_GHOST:
1648     break;
1649   default:
1650     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1651   }
1652   PetscFunctionReturn(PETSC_SUCCESS);
1653 }
1654 
1655 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1656 {
1657   DMPolytopeType ct;
1658   PetscReal      centroid[2] = {0., 0.};
1659   PetscMPIInt    rank;
1660   PetscInt       fillColor, v, e, d;
1661 
1662   PetscFunctionBegin;
1663   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1664   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1665   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1666   switch (ct) {
1667   case DM_POLYTOPE_TRIANGLE: {
1668     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1669 
1670     for (v = 0; v < 3; ++v) {
1671       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1672       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1673     }
1674     for (e = 0; e < 3; ++e) {
1675       refCoords[0] = refVertices[e * 2 + 0];
1676       refCoords[1] = refVertices[e * 2 + 1];
1677       for (d = 1; d <= edgeDiv; ++d) {
1678         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1679         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1680       }
1681       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1682       for (d = 0; d < edgeDiv; ++d) {
1683         PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1684         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1685       }
1686     }
1687   } break;
1688   default:
1689     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1690   }
1691   PetscFunctionReturn(PETSC_SUCCESS);
1692 }
1693 
1694 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1695 {
1696   PetscDraw    draw;
1697   DM           cdm;
1698   PetscSection coordSection;
1699   Vec          coordinates;
1700   PetscReal    xyl[3], xyr[3];
1701   PetscReal   *refCoords, *edgeCoords;
1702   PetscBool    isnull, drawAffine = PETSC_TRUE;
1703   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1704 
1705   PetscFunctionBegin;
1706   PetscCall(DMGetCoordinateDim(dm, &dim));
1707   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1708   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1709   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1710   PetscCall(DMGetCoordinateDM(dm, &cdm));
1711   PetscCall(DMGetLocalSection(cdm, &coordSection));
1712   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1713   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1714   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1715 
1716   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1717   PetscCall(PetscDrawIsNull(draw, &isnull));
1718   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1719   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1720 
1721   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1722   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1723   PetscCall(PetscDrawClear(draw));
1724 
1725   for (c = cStart; c < cEnd; ++c) {
1726     PetscScalar       *coords = NULL;
1727     const PetscScalar *coords_arr;
1728     PetscInt           numCoords;
1729     PetscBool          isDG;
1730 
1731     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1732     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1733     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1734     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1735   }
1736   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1737   PetscCall(PetscDrawFlush(draw));
1738   PetscCall(PetscDrawPause(draw));
1739   PetscCall(PetscDrawSave(draw));
1740   PetscFunctionReturn(PETSC_SUCCESS);
1741 }
1742 
1743 #if defined(PETSC_HAVE_EXODUSII)
1744   #include <exodusII.h>
1745   #include <petscviewerexodusii.h>
1746 #endif
1747 
1748 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1749 {
1750   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1751   char      name[PETSC_MAX_PATH_LEN];
1752 
1753   PetscFunctionBegin;
1754   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1755   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1756   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1757   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1759   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1761   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1762   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1763   if (iascii) {
1764     PetscViewerFormat format;
1765     PetscCall(PetscViewerGetFormat(viewer, &format));
1766     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1767     else PetscCall(DMPlexView_Ascii(dm, viewer));
1768   } else if (ishdf5) {
1769 #if defined(PETSC_HAVE_HDF5)
1770     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1771 #else
1772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1773 #endif
1774   } else if (isvtk) {
1775     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1776   } else if (isdraw) {
1777     PetscCall(DMPlexView_Draw(dm, viewer));
1778   } else if (isglvis) {
1779     PetscCall(DMPlexView_GLVis(dm, viewer));
1780 #if defined(PETSC_HAVE_EXODUSII)
1781   } else if (isexodus) {
1782     /*
1783       exodusII requires that all sets be part of exactly one cell set.
1784       If the dm does not have a "Cell Sets" label defined, we create one
1785       with ID 1, containing all cells.
1786       Note that if the Cell Sets label is defined but does not cover all cells,
1787       we may still have a problem. This should probably be checked here or in the viewer;
1788     */
1789     PetscInt numCS;
1790     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1791     if (!numCS) {
1792       PetscInt cStart, cEnd, c;
1793       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1794       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1795       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1796     }
1797     PetscCall(DMView_PlexExodusII(dm, viewer));
1798 #endif
1799 #if defined(PETSC_HAVE_CGNS)
1800   } else if (iscgns) {
1801     PetscCall(DMView_PlexCGNS(dm, viewer));
1802 #endif
1803   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1804   /* Optionally view the partition */
1805   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1806   if (flg) {
1807     Vec ranks;
1808     PetscCall(DMPlexCreateRankField(dm, &ranks));
1809     PetscCall(VecView(ranks, viewer));
1810     PetscCall(VecDestroy(&ranks));
1811   }
1812   /* Optionally view a label */
1813   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1814   if (flg) {
1815     DMLabel label;
1816     Vec     val;
1817 
1818     PetscCall(DMGetLabel(dm, name, &label));
1819     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1820     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1821     PetscCall(VecView(val, viewer));
1822     PetscCall(VecDestroy(&val));
1823   }
1824   PetscFunctionReturn(PETSC_SUCCESS);
1825 }
1826 
1827 /*@
1828   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1829 
1830   Collective
1831 
1832   Input Parameters:
1833 + dm     - The `DM` whose topology is to be saved
1834 - viewer - The `PetscViewer` to save it in
1835 
1836   Level: advanced
1837 
1838 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1839 @*/
1840 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1841 {
1842   PetscBool ishdf5;
1843 
1844   PetscFunctionBegin;
1845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1846   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1847   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1848   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1849   if (ishdf5) {
1850 #if defined(PETSC_HAVE_HDF5)
1851     PetscViewerFormat format;
1852     PetscCall(PetscViewerGetFormat(viewer, &format));
1853     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1854       IS globalPointNumbering;
1855 
1856       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1857       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1858       PetscCall(ISDestroy(&globalPointNumbering));
1859     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1860 #else
1861     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1862 #endif
1863   }
1864   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 /*@
1869   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1870 
1871   Collective
1872 
1873   Input Parameters:
1874 + dm     - The `DM` whose coordinates are to be saved
1875 - viewer - The `PetscViewer` for saving
1876 
1877   Level: advanced
1878 
1879 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1880 @*/
1881 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1882 {
1883   PetscBool ishdf5;
1884 
1885   PetscFunctionBegin;
1886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1887   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1888   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1889   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1890   if (ishdf5) {
1891 #if defined(PETSC_HAVE_HDF5)
1892     PetscViewerFormat format;
1893     PetscCall(PetscViewerGetFormat(viewer, &format));
1894     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1895       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1896     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1897 #else
1898     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1899 #endif
1900   }
1901   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1902   PetscFunctionReturn(PETSC_SUCCESS);
1903 }
1904 
1905 /*@
1906   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1907 
1908   Collective
1909 
1910   Input Parameters:
1911 + dm     - The `DM` whose labels are to be saved
1912 - viewer - The `PetscViewer` for saving
1913 
1914   Level: advanced
1915 
1916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1917 @*/
1918 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1919 {
1920   PetscBool ishdf5;
1921 
1922   PetscFunctionBegin;
1923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1924   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1925   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1926   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1927   if (ishdf5) {
1928 #if defined(PETSC_HAVE_HDF5)
1929     IS                globalPointNumbering;
1930     PetscViewerFormat format;
1931 
1932     PetscCall(PetscViewerGetFormat(viewer, &format));
1933     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1934       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1935       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1936       PetscCall(ISDestroy(&globalPointNumbering));
1937     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1938 #else
1939     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1940 #endif
1941   }
1942   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1943   PetscFunctionReturn(PETSC_SUCCESS);
1944 }
1945 
1946 /*@
1947   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1948 
1949   Collective
1950 
1951   Input Parameters:
1952 + dm         - The `DM` that contains the topology on which the section to be saved is defined
1953 . viewer     - The `PetscViewer` for saving
1954 - sectiondm  - The `DM` that contains the section to be saved
1955 
1956   Level: advanced
1957 
1958   Notes:
1959   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
1960 
1961   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1962 
1963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1964 @*/
1965 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1966 {
1967   PetscBool ishdf5;
1968 
1969   PetscFunctionBegin;
1970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1971   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1972   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1973   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1974   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1975   if (ishdf5) {
1976 #if defined(PETSC_HAVE_HDF5)
1977     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1978 #else
1979     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1980 #endif
1981   }
1982   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1983   PetscFunctionReturn(PETSC_SUCCESS);
1984 }
1985 
1986 /*@
1987   DMPlexGlobalVectorView - Saves a global vector
1988 
1989   Collective
1990 
1991   Input Parameters:
1992 + dm        - The `DM` that represents the topology
1993 . viewer    - The `PetscViewer` to save data with
1994 . sectiondm - The `DM` that contains the global section on which vec is defined
1995 - vec       - The global vector to be saved
1996 
1997   Level: advanced
1998 
1999   Notes:
2000   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2001 
2002   Typical calling sequence:
2003 .vb
2004        DMCreate(PETSC_COMM_WORLD, &dm);
2005        DMSetType(dm, DMPLEX);
2006        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2007        DMClone(dm, &sectiondm);
2008        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2009        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2010        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2011        PetscSectionSetChart(section, pStart, pEnd);
2012        PetscSectionSetUp(section);
2013        DMSetLocalSection(sectiondm, section);
2014        PetscSectionDestroy(&section);
2015        DMGetGlobalVector(sectiondm, &vec);
2016        PetscObjectSetName((PetscObject)vec, "vec_name");
2017        DMPlexTopologyView(dm, viewer);
2018        DMPlexSectionView(dm, viewer, sectiondm);
2019        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2020        DMRestoreGlobalVector(sectiondm, &vec);
2021        DMDestroy(&sectiondm);
2022        DMDestroy(&dm);
2023 .ve
2024 
2025 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2026 @*/
2027 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2028 {
2029   PetscBool ishdf5;
2030 
2031   PetscFunctionBegin;
2032   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2033   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2034   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2035   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2036   /* Check consistency */
2037   {
2038     PetscSection section;
2039     PetscBool    includesConstraints;
2040     PetscInt     m, m1;
2041 
2042     PetscCall(VecGetLocalSize(vec, &m1));
2043     PetscCall(DMGetGlobalSection(sectiondm, &section));
2044     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2045     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2046     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2047     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2048   }
2049   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2050   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2051   if (ishdf5) {
2052 #if defined(PETSC_HAVE_HDF5)
2053     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2054 #else
2055     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2056 #endif
2057   }
2058   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2059   PetscFunctionReturn(PETSC_SUCCESS);
2060 }
2061 
2062 /*@
2063   DMPlexLocalVectorView - Saves a local vector
2064 
2065   Collective
2066 
2067   Input Parameters:
2068 + dm        - The `DM` that represents the topology
2069 . viewer    - The `PetscViewer` to save data with
2070 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2071 - vec       - The local vector to be saved
2072 
2073   Level: advanced
2074 
2075   Note:
2076   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2077 
2078   Typical calling sequence:
2079 .vb
2080        DMCreate(PETSC_COMM_WORLD, &dm);
2081        DMSetType(dm, DMPLEX);
2082        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2083        DMClone(dm, &sectiondm);
2084        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2085        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2086        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2087        PetscSectionSetChart(section, pStart, pEnd);
2088        PetscSectionSetUp(section);
2089        DMSetLocalSection(sectiondm, section);
2090        DMGetLocalVector(sectiondm, &vec);
2091        PetscObjectSetName((PetscObject)vec, "vec_name");
2092        DMPlexTopologyView(dm, viewer);
2093        DMPlexSectionView(dm, viewer, sectiondm);
2094        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2095        DMRestoreLocalVector(sectiondm, &vec);
2096        DMDestroy(&sectiondm);
2097        DMDestroy(&dm);
2098 .ve
2099 
2100 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2101 @*/
2102 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2103 {
2104   PetscBool ishdf5;
2105 
2106   PetscFunctionBegin;
2107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2108   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2109   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2110   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2111   /* Check consistency */
2112   {
2113     PetscSection section;
2114     PetscBool    includesConstraints;
2115     PetscInt     m, m1;
2116 
2117     PetscCall(VecGetLocalSize(vec, &m1));
2118     PetscCall(DMGetLocalSection(sectiondm, &section));
2119     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2120     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2121     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2122     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2123   }
2124   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2125   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2126   if (ishdf5) {
2127 #if defined(PETSC_HAVE_HDF5)
2128     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2129 #else
2130     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2131 #endif
2132   }
2133   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2134   PetscFunctionReturn(PETSC_SUCCESS);
2135 }
2136 
2137 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2138 {
2139   PetscBool ishdf5;
2140 
2141   PetscFunctionBegin;
2142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2143   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2144   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2145   if (ishdf5) {
2146 #if defined(PETSC_HAVE_HDF5)
2147     PetscViewerFormat format;
2148     PetscCall(PetscViewerGetFormat(viewer, &format));
2149     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2150       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2151     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2152       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2153     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2154     PetscFunctionReturn(PETSC_SUCCESS);
2155 #else
2156     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2157 #endif
2158   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2159 }
2160 
2161 /*@
2162   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2163 
2164   Collective
2165 
2166   Input Parameters:
2167 + dm                - The `DM` into which the topology is loaded
2168 - viewer            - The `PetscViewer` for the saved topology
2169 
2170   Output Parameter:
2171 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2172 
2173   Level: advanced
2174 
2175 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2176           `PetscViewer`, `PetscSF`
2177 @*/
2178 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2179 {
2180   PetscBool ishdf5;
2181 
2182   PetscFunctionBegin;
2183   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2184   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2185   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2186   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2187   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2188   if (ishdf5) {
2189 #if defined(PETSC_HAVE_HDF5)
2190     PetscViewerFormat format;
2191     PetscCall(PetscViewerGetFormat(viewer, &format));
2192     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2193       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2194     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2195 #else
2196     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2197 #endif
2198   }
2199   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2200   PetscFunctionReturn(PETSC_SUCCESS);
2201 }
2202 
2203 /*@
2204   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2205 
2206   Collective
2207 
2208   Input Parameters:
2209 + dm     - The `DM` into which the coordinates are loaded
2210 . viewer - The `PetscViewer` for the saved coordinates
2211 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2212 
2213   Level: advanced
2214 
2215 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2216           `PetscSF`, `PetscViewer`
2217 @*/
2218 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2219 {
2220   PetscBool ishdf5;
2221 
2222   PetscFunctionBegin;
2223   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2224   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2225   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2226   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2227   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2228   if (ishdf5) {
2229 #if defined(PETSC_HAVE_HDF5)
2230     PetscViewerFormat format;
2231     PetscCall(PetscViewerGetFormat(viewer, &format));
2232     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2233       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2234     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2235 #else
2236     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2237 #endif
2238   }
2239   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2240   PetscFunctionReturn(PETSC_SUCCESS);
2241 }
2242 
2243 /*@
2244   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2245 
2246   Collective
2247 
2248   Input Parameters:
2249 + dm     - The `DM` into which the labels are loaded
2250 . viewer - The `PetscViewer` for the saved labels
2251 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2252 
2253   Level: advanced
2254 
2255   Note:
2256   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2257 
2258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2259           `PetscSF`, `PetscViewer`
2260 @*/
2261 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2262 {
2263   PetscBool ishdf5;
2264 
2265   PetscFunctionBegin;
2266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2267   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2268   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2269   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2270   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2271   if (ishdf5) {
2272 #if defined(PETSC_HAVE_HDF5)
2273     PetscViewerFormat format;
2274 
2275     PetscCall(PetscViewerGetFormat(viewer, &format));
2276     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2277       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2278     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2279 #else
2280     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2281 #endif
2282   }
2283   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2284   PetscFunctionReturn(PETSC_SUCCESS);
2285 }
2286 
2287 /*@
2288   DMPlexSectionLoad - Loads section into a `DMPLEX`
2289 
2290   Collective
2291 
2292   Input Parameters:
2293 + dm          - The `DM` that represents the topology
2294 . viewer      - The `PetscViewer` that represents the on-disk section (sectionA)
2295 . sectiondm   - The `DM` into which the on-disk section (sectionA) is migrated
2296 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2297 
2298   Output Parameters
2299 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2300 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2301 
2302   Level: advanced
2303 
2304   Notes:
2305   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2306 
2307   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2308 
2309   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2310 
2311   Example using 2 processes:
2312 .vb
2313   NX (number of points on dm): 4
2314   sectionA                   : the on-disk section
2315   vecA                       : a vector associated with sectionA
2316   sectionB                   : sectiondm's local section constructed in this function
2317   vecB (local)               : a vector associated with sectiondm's local section
2318   vecB (global)              : a vector associated with sectiondm's global section
2319 
2320                                      rank 0    rank 1
2321   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2322   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2323   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2324   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2325   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2326   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2327   sectionB->atlasDof             :     1 0 1 | 1 3
2328   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2329   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2330   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2331 .ve
2332   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2333 
2334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2335 @*/
2336 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2344   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2345   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2346   if (localDofSF) PetscValidPointer(localDofSF, 6);
2347   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2348   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2349   if (ishdf5) {
2350 #if defined(PETSC_HAVE_HDF5)
2351     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2352 #else
2353     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2354 #endif
2355   }
2356   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2357   PetscFunctionReturn(PETSC_SUCCESS);
2358 }
2359 
2360 /*@
2361   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2362 
2363   Collective
2364 
2365   Input Parameters:
2366 + dm        - The `DM` that represents the topology
2367 . viewer    - The `PetscViewer` that represents the on-disk vector data
2368 . sectiondm - The `DM` that contains the global section on which vec is defined
2369 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2370 - vec       - The global vector to set values of
2371 
2372   Level: advanced
2373 
2374   Notes:
2375   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2376 
2377   Typical calling sequence:
2378 .vb
2379        DMCreate(PETSC_COMM_WORLD, &dm);
2380        DMSetType(dm, DMPLEX);
2381        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2382        DMPlexTopologyLoad(dm, viewer, &sfX);
2383        DMClone(dm, &sectiondm);
2384        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2385        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2386        DMGetGlobalVector(sectiondm, &vec);
2387        PetscObjectSetName((PetscObject)vec, "vec_name");
2388        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2389        DMRestoreGlobalVector(sectiondm, &vec);
2390        PetscSFDestroy(&gsf);
2391        PetscSFDestroy(&sfX);
2392        DMDestroy(&sectiondm);
2393        DMDestroy(&dm);
2394 .ve
2395 
2396 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2397           `PetscSF`, `PetscViewer`
2398 @*/
2399 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2400 {
2401   PetscBool ishdf5;
2402 
2403   PetscFunctionBegin;
2404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2405   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2406   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2407   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2408   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2409   /* Check consistency */
2410   {
2411     PetscSection section;
2412     PetscBool    includesConstraints;
2413     PetscInt     m, m1;
2414 
2415     PetscCall(VecGetLocalSize(vec, &m1));
2416     PetscCall(DMGetGlobalSection(sectiondm, &section));
2417     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2418     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2419     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2420     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2421   }
2422   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2423   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2424   if (ishdf5) {
2425 #if defined(PETSC_HAVE_HDF5)
2426     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2427 #else
2428     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2429 #endif
2430   }
2431   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2432   PetscFunctionReturn(PETSC_SUCCESS);
2433 }
2434 
2435 /*@
2436   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2437 
2438   Collective
2439 
2440   Input Parameters:
2441 + dm        - The `DM` that represents the topology
2442 . viewer    - The `PetscViewer` that represents the on-disk vector data
2443 . sectiondm - The `DM` that contains the local section on which vec is defined
2444 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2445 - vec       - The local vector to set values of
2446 
2447   Level: advanced
2448 
2449   Notes:
2450   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2451 
2452   Typical calling sequence:
2453 .vb
2454        DMCreate(PETSC_COMM_WORLD, &dm);
2455        DMSetType(dm, DMPLEX);
2456        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2457        DMPlexTopologyLoad(dm, viewer, &sfX);
2458        DMClone(dm, &sectiondm);
2459        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2460        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2461        DMGetLocalVector(sectiondm, &vec);
2462        PetscObjectSetName((PetscObject)vec, "vec_name");
2463        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2464        DMRestoreLocalVector(sectiondm, &vec);
2465        PetscSFDestroy(&lsf);
2466        PetscSFDestroy(&sfX);
2467        DMDestroy(&sectiondm);
2468        DMDestroy(&dm);
2469 .ve
2470 
2471 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2472           `PetscSF`, `PetscViewer`
2473 @*/
2474 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2475 {
2476   PetscBool ishdf5;
2477 
2478   PetscFunctionBegin;
2479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2480   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2481   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2482   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2483   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2484   /* Check consistency */
2485   {
2486     PetscSection section;
2487     PetscBool    includesConstraints;
2488     PetscInt     m, m1;
2489 
2490     PetscCall(VecGetLocalSize(vec, &m1));
2491     PetscCall(DMGetLocalSection(sectiondm, &section));
2492     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2493     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2494     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2495     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2496   }
2497   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2498   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2499   if (ishdf5) {
2500 #if defined(PETSC_HAVE_HDF5)
2501     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2502 #else
2503     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2504 #endif
2505   }
2506   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2507   PetscFunctionReturn(PETSC_SUCCESS);
2508 }
2509 
2510 PetscErrorCode DMDestroy_Plex(DM dm)
2511 {
2512   DM_Plex *mesh = (DM_Plex *)dm->data;
2513 
2514   PetscFunctionBegin;
2515   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2516   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2517   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2518   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2519   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2520   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2521   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2522   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2523   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2524   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2525   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2526   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2527   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2528   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2529   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2530   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2531   PetscCall(PetscFree(mesh->cones));
2532   PetscCall(PetscFree(mesh->coneOrientations));
2533   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2534   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2535   PetscCall(PetscFree(mesh->supports));
2536   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2537   PetscCall(PetscFree(mesh->facesTmp));
2538   PetscCall(PetscFree(mesh->tetgenOpts));
2539   PetscCall(PetscFree(mesh->triangleOpts));
2540   PetscCall(PetscFree(mesh->transformType));
2541   PetscCall(PetscFree(mesh->distributionName));
2542   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2543   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2544   PetscCall(ISDestroy(&mesh->subpointIS));
2545   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2546   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2547   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2548   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2549   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2550   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2551   PetscCall(ISDestroy(&mesh->anchorIS));
2552   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2553   PetscCall(PetscFree(mesh->parents));
2554   PetscCall(PetscFree(mesh->childIDs));
2555   PetscCall(PetscSectionDestroy(&mesh->childSection));
2556   PetscCall(PetscFree(mesh->children));
2557   PetscCall(DMDestroy(&mesh->referenceTree));
2558   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2559   PetscCall(PetscFree(mesh->neighbors));
2560   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2561   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2562   PetscCall(PetscFree(mesh));
2563   PetscFunctionReturn(PETSC_SUCCESS);
2564 }
2565 
2566 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2567 {
2568   PetscSection           sectionGlobal;
2569   PetscInt               bs = -1, mbs;
2570   PetscInt               localSize, localStart = 0;
2571   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2572   MatType                mtype;
2573   ISLocalToGlobalMapping ltog;
2574 
2575   PetscFunctionBegin;
2576   PetscCall(MatInitializePackage());
2577   mtype = dm->mattype;
2578   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2579   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2580   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2581   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2582   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2583   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2584   PetscCall(MatSetType(*J, mtype));
2585   PetscCall(MatSetFromOptions(*J));
2586   PetscCall(MatGetBlockSize(*J, &mbs));
2587   if (mbs > 1) bs = mbs;
2588   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2589   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2590   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2591   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2592   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2593   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2594   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2595   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2596   if (!isShell) {
2597     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2598     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2599     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2600 
2601     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2602 
2603     PetscCall(PetscCalloc1(localSize, &pblocks));
2604     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2605     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2606     for (p = pStart; p < pEnd; ++p) {
2607       switch (dm->blocking_type) {
2608       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2609         PetscInt bdof, offset;
2610 
2611         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2612         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2613         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2614         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2615         dof  = dof < 0 ? -(dof + 1) : dof;
2616         bdof = cdof && (dof - cdof) ? 1 : dof;
2617         if (dof) {
2618           if (bs < 0) {
2619             bs = bdof;
2620           } else if (bs != bdof) {
2621             bs = 1;
2622           }
2623         }
2624       } break;
2625       case DM_BLOCKING_FIELD_NODE: {
2626         for (PetscInt field = 0; field < num_fields; field++) {
2627           PetscInt num_comp, bdof, offset;
2628           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2629           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2630           if (dof < 0) continue;
2631           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2632           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2633           PetscAssert(dof % num_comp == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " field %" PetscInt_FMT " has %" PetscInt_FMT " dof, not divisible by %" PetscInt_FMT " component ", p, field, dof, num_comp);
2634           PetscInt num_nodes = dof / num_comp;
2635           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2636           // Handle possibly constant block size (unlikely)
2637           bdof = cdof && (dof - cdof) ? 1 : dof;
2638           if (dof) {
2639             if (bs < 0) {
2640               bs = bdof;
2641             } else if (bs != bdof) {
2642               bs = 1;
2643             }
2644           }
2645         }
2646       } break;
2647       }
2648     }
2649     /* Must have same blocksize on all procs (some might have no points) */
2650     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2651     bsLocal[1] = bs;
2652     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2653     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2654     else bs = bsMinMax[0];
2655     bs = PetscMax(1, bs);
2656     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2657     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2658       PetscCall(MatSetBlockSize(*J, bs));
2659       PetscCall(MatSetUp(*J));
2660     } else {
2661       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2662       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2663       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2664     }
2665     { // Consolidate blocks
2666       PetscInt nblocks = 0;
2667       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2668         if (pblocks[i] == 0) continue;
2669         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2670         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2671       }
2672       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2673     }
2674     PetscCall(PetscFree(pblocks));
2675   }
2676   PetscCall(MatSetDM(*J, dm));
2677   PetscFunctionReturn(PETSC_SUCCESS);
2678 }
2679 
2680 /*@
2681   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2682 
2683   Not Collective
2684 
2685   Input Parameter:
2686 . mesh - The `DMPLEX`
2687 
2688   Output Parameter:
2689 . subsection - The subdomain section
2690 
2691   Level: developer
2692 
2693 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2694 @*/
2695 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2696 {
2697   DM_Plex *mesh = (DM_Plex *)dm->data;
2698 
2699   PetscFunctionBegin;
2700   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2701   if (!mesh->subdomainSection) {
2702     PetscSection section;
2703     PetscSF      sf;
2704 
2705     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2706     PetscCall(DMGetLocalSection(dm, &section));
2707     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2708     PetscCall(PetscSFDestroy(&sf));
2709   }
2710   *subsection = mesh->subdomainSection;
2711   PetscFunctionReturn(PETSC_SUCCESS);
2712 }
2713 
2714 /*@
2715   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2716 
2717   Not Collective
2718 
2719   Input Parameter:
2720 . mesh - The `DMPLEX`
2721 
2722   Output Parameters:
2723 + pStart - The first mesh point
2724 - pEnd   - The upper bound for mesh points
2725 
2726   Level: beginner
2727 
2728 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2729 @*/
2730 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2731 {
2732   DM_Plex *mesh = (DM_Plex *)dm->data;
2733 
2734   PetscFunctionBegin;
2735   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2736   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2737   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2738   PetscFunctionReturn(PETSC_SUCCESS);
2739 }
2740 
2741 /*@
2742   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2743 
2744   Not Collective
2745 
2746   Input Parameters:
2747 + mesh - The `DMPLEX`
2748 . pStart - The first mesh point
2749 - pEnd   - The upper bound for mesh points
2750 
2751   Level: beginner
2752 
2753 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2754 @*/
2755 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2756 {
2757   DM_Plex *mesh = (DM_Plex *)dm->data;
2758 
2759   PetscFunctionBegin;
2760   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2761   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2762   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2763   PetscFunctionReturn(PETSC_SUCCESS);
2764 }
2765 
2766 /*@
2767   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2768 
2769   Not Collective
2770 
2771   Input Parameters:
2772 + mesh - The `DMPLEX`
2773 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2774 
2775   Output Parameter:
2776 . size - The cone size for point `p`
2777 
2778   Level: beginner
2779 
2780 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2781 @*/
2782 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2783 {
2784   DM_Plex *mesh = (DM_Plex *)dm->data;
2785 
2786   PetscFunctionBegin;
2787   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2788   PetscValidIntPointer(size, 3);
2789   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2790   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2791   PetscFunctionReturn(PETSC_SUCCESS);
2792 }
2793 
2794 /*@
2795   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2796 
2797   Not Collective
2798 
2799   Input Parameters:
2800 + mesh - The `DMPLEX`
2801 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2802 - size - The cone size for point `p`
2803 
2804   Level: beginner
2805 
2806   Note:
2807   This should be called after `DMPlexSetChart()`.
2808 
2809 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2810 @*/
2811 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2812 {
2813   DM_Plex *mesh = (DM_Plex *)dm->data;
2814 
2815   PetscFunctionBegin;
2816   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2817   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2818   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2819   PetscFunctionReturn(PETSC_SUCCESS);
2820 }
2821 
2822 /*@C
2823   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2824 
2825   Not Collective
2826 
2827   Input Parameters:
2828 + dm - The `DMPLEX`
2829 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2830 
2831   Output Parameter:
2832 . cone - An array of points which are on the in-edges for point `p`
2833 
2834   Level: beginner
2835 
2836   Fortran Note:
2837   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2838   `DMPlexRestoreCone()` is not needed/available in C.
2839 
2840 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2841 @*/
2842 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2843 {
2844   DM_Plex *mesh = (DM_Plex *)dm->data;
2845   PetscInt off;
2846 
2847   PetscFunctionBegin;
2848   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2849   PetscValidPointer(cone, 3);
2850   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2851   *cone = &mesh->cones[off];
2852   PetscFunctionReturn(PETSC_SUCCESS);
2853 }
2854 
2855 /*@C
2856   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2857 
2858   Not Collective
2859 
2860   Input Parameters:
2861 + dm - The `DMPLEX`
2862 - p - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2863 
2864   Output Parameters:
2865 + pConesSection - `PetscSection` describing the layout of `pCones`
2866 - pCones - An array of points which are on the in-edges for the point set `p`
2867 
2868   Level: intermediate
2869 
2870 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2871 @*/
2872 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2873 {
2874   PetscSection cs, newcs;
2875   PetscInt    *cones;
2876   PetscInt    *newarr = NULL;
2877   PetscInt     n;
2878 
2879   PetscFunctionBegin;
2880   PetscCall(DMPlexGetCones(dm, &cones));
2881   PetscCall(DMPlexGetConeSection(dm, &cs));
2882   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2883   if (pConesSection) *pConesSection = newcs;
2884   if (pCones) {
2885     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2886     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2887   }
2888   PetscFunctionReturn(PETSC_SUCCESS);
2889 }
2890 
2891 /*@
2892   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2893 
2894   Not Collective
2895 
2896   Input Parameters:
2897 + dm - The `DMPLEX`
2898 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2899 
2900   Output Parameter:
2901 . expandedPoints - An array of vertices recursively expanded from input points
2902 
2903   Level: advanced
2904 
2905   Notes:
2906   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
2907 
2908   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2909 
2910 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2911           `DMPlexGetDepth()`, `IS`
2912 @*/
2913 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2914 {
2915   IS      *expandedPointsAll;
2916   PetscInt depth;
2917 
2918   PetscFunctionBegin;
2919   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2920   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2921   PetscValidPointer(expandedPoints, 3);
2922   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2923   *expandedPoints = expandedPointsAll[0];
2924   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2925   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2926   PetscFunctionReturn(PETSC_SUCCESS);
2927 }
2928 
2929 /*@
2930   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).
2931 
2932   Not Collective
2933 
2934   Input Parameters:
2935 + dm - The `DMPLEX`
2936 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2937 
2938   Output Parameters:
2939 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2940 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2941 - sections - (optional) An array of sections which describe mappings from points to their cone points
2942 
2943   Level: advanced
2944 
2945   Notes:
2946   Like `DMPlexGetConeTuple()` but recursive.
2947 
2948   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.
2949   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2950 
2951   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:
2952   (1) DAG points in expandedPoints[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
2953   (2) DAG points in expandedPoints[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
2954 
2955 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2956           `DMPlexGetDepth()`, `PetscSection`, `IS`
2957 @*/
2958 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2959 {
2960   const PetscInt *arr0 = NULL, *cone = NULL;
2961   PetscInt       *arr = NULL, *newarr = NULL;
2962   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2963   IS             *expandedPoints_;
2964   PetscSection   *sections_;
2965 
2966   PetscFunctionBegin;
2967   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2968   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2969   if (depth) PetscValidIntPointer(depth, 3);
2970   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2971   if (sections) PetscValidPointer(sections, 5);
2972   PetscCall(ISGetLocalSize(points, &n));
2973   PetscCall(ISGetIndices(points, &arr0));
2974   PetscCall(DMPlexGetDepth(dm, &depth_));
2975   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2976   PetscCall(PetscCalloc1(depth_, &sections_));
2977   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2978   for (d = depth_ - 1; d >= 0; d--) {
2979     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2980     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2981     for (i = 0; i < n; i++) {
2982       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2983       if (arr[i] >= start && arr[i] < end) {
2984         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2985         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2986       } else {
2987         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2988       }
2989     }
2990     PetscCall(PetscSectionSetUp(sections_[d]));
2991     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2992     PetscCall(PetscMalloc1(newn, &newarr));
2993     for (i = 0; i < n; i++) {
2994       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2995       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2996       if (cn > 1) {
2997         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2998         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2999       } else {
3000         newarr[co] = arr[i];
3001       }
3002     }
3003     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3004     arr = newarr;
3005     n   = newn;
3006   }
3007   PetscCall(ISRestoreIndices(points, &arr0));
3008   *depth = depth_;
3009   if (expandedPoints) *expandedPoints = expandedPoints_;
3010   else {
3011     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3012     PetscCall(PetscFree(expandedPoints_));
3013   }
3014   if (sections) *sections = sections_;
3015   else {
3016     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3017     PetscCall(PetscFree(sections_));
3018   }
3019   PetscFunctionReturn(PETSC_SUCCESS);
3020 }
3021 
3022 /*@
3023   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3024 
3025   Not Collective
3026 
3027   Input Parameters:
3028 + dm - The `DMPLEX`
3029 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3030 
3031   Output Parameters:
3032 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3033 . expandedPoints - (optional) An array of recursively expanded cones
3034 - sections - (optional) An array of sections which describe mappings from points to their cone points
3035 
3036   Level: advanced
3037 
3038   Note:
3039   See `DMPlexGetConeRecursive()`
3040 
3041 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3042           `DMPlexGetDepth()`, `IS`, `PetscSection`
3043 @*/
3044 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3045 {
3046   PetscInt d, depth_;
3047 
3048   PetscFunctionBegin;
3049   PetscCall(DMPlexGetDepth(dm, &depth_));
3050   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3051   if (depth) *depth = 0;
3052   if (expandedPoints) {
3053     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3054     PetscCall(PetscFree(*expandedPoints));
3055   }
3056   if (sections) {
3057     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3058     PetscCall(PetscFree(*sections));
3059   }
3060   PetscFunctionReturn(PETSC_SUCCESS);
3061 }
3062 
3063 /*@
3064   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
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 - cone - An array of points which are on the in-edges for point `p`
3072 
3073   Level: beginner
3074 
3075   Note:
3076   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3077 
3078 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3079 @*/
3080 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3081 {
3082   DM_Plex *mesh = (DM_Plex *)dm->data;
3083   PetscInt pStart, pEnd;
3084   PetscInt dof, off, c;
3085 
3086   PetscFunctionBegin;
3087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3088   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3089   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3090   if (dof) PetscValidIntPointer(cone, 3);
3091   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3092   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);
3093   for (c = 0; c < dof; ++c) {
3094     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);
3095     mesh->cones[off + c] = cone[c];
3096   }
3097   PetscFunctionReturn(PETSC_SUCCESS);
3098 }
3099 
3100 /*@C
3101   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3102 
3103   Not Collective
3104 
3105   Input Parameters:
3106 + mesh - The `DMPLEX`
3107 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3108 
3109   Output Parameter:
3110 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3111                     integer giving the prescription for cone traversal.
3112 
3113   Level: beginner
3114 
3115   Note:
3116   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3117   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3118   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3119   with the identity.
3120 
3121   Fortran Note:
3122   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3123   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3124 
3125 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3126 @*/
3127 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3128 {
3129   DM_Plex *mesh = (DM_Plex *)dm->data;
3130   PetscInt off;
3131 
3132   PetscFunctionBegin;
3133   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3134   if (PetscDefined(USE_DEBUG)) {
3135     PetscInt dof;
3136     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3137     if (dof) PetscValidPointer(coneOrientation, 3);
3138   }
3139   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3140 
3141   *coneOrientation = &mesh->coneOrientations[off];
3142   PetscFunctionReturn(PETSC_SUCCESS);
3143 }
3144 
3145 /*@
3146   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3147 
3148   Not Collective
3149 
3150   Input Parameters:
3151 + mesh - The `DMPLEX`
3152 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3153 - coneOrientation - An array of orientations
3154 
3155   Level: beginner
3156 
3157   Notes:
3158   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3159 
3160   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3161 
3162 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3163 @*/
3164 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3165 {
3166   DM_Plex *mesh = (DM_Plex *)dm->data;
3167   PetscInt pStart, pEnd;
3168   PetscInt dof, off, c;
3169 
3170   PetscFunctionBegin;
3171   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3172   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3173   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3174   if (dof) PetscValidIntPointer(coneOrientation, 3);
3175   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3176   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);
3177   for (c = 0; c < dof; ++c) {
3178     PetscInt cdof, o = coneOrientation[c];
3179 
3180     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3181     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);
3182     mesh->coneOrientations[off + c] = o;
3183   }
3184   PetscFunctionReturn(PETSC_SUCCESS);
3185 }
3186 
3187 /*@
3188   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3189 
3190   Not Collective
3191 
3192   Input Parameters:
3193 + mesh - The `DMPLEX`
3194 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3195 . conePos - The local index in the cone where the point should be put
3196 - conePoint - The mesh point to insert
3197 
3198   Level: beginner
3199 
3200 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3201 @*/
3202 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3203 {
3204   DM_Plex *mesh = (DM_Plex *)dm->data;
3205   PetscInt pStart, pEnd;
3206   PetscInt dof, off;
3207 
3208   PetscFunctionBegin;
3209   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3210   if (PetscDefined(USE_DEBUG)) {
3211     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3212     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);
3213     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);
3214     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3215     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);
3216   }
3217   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3218   mesh->cones[off + conePos] = conePoint;
3219   PetscFunctionReturn(PETSC_SUCCESS);
3220 }
3221 
3222 /*@
3223   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3224 
3225   Not Collective
3226 
3227   Input Parameters:
3228 + mesh - The `DMPLEX`
3229 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3230 . conePos - The local index in the cone where the point should be put
3231 - coneOrientation - The point orientation to insert
3232 
3233   Level: beginner
3234 
3235   Note:
3236   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3237 
3238 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3239 @*/
3240 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3241 {
3242   DM_Plex *mesh = (DM_Plex *)dm->data;
3243   PetscInt pStart, pEnd;
3244   PetscInt dof, off;
3245 
3246   PetscFunctionBegin;
3247   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3248   if (PetscDefined(USE_DEBUG)) {
3249     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3250     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);
3251     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3252     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);
3253   }
3254   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3255   mesh->coneOrientations[off + conePos] = coneOrientation;
3256   PetscFunctionReturn(PETSC_SUCCESS);
3257 }
3258 
3259 /*@C
3260   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3261 
3262   Not collective
3263 
3264   Input Parameters:
3265 + dm - The DMPlex
3266 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3267 
3268   Output Parameters:
3269 + cone - An array of points which are on the in-edges for point `p`
3270 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3271         integer giving the prescription for cone traversal.
3272 
3273   Level: beginner
3274 
3275   Notes:
3276   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3277   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3278   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3279   with the identity.
3280 
3281   Fortran Notes:
3282   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3283   `DMPlexRestoreCone()` is not needed/available in C.
3284 
3285 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3286 @*/
3287 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3288 {
3289   DM_Plex *mesh = (DM_Plex *)dm->data;
3290 
3291   PetscFunctionBegin;
3292   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3293   if (mesh->tr) {
3294     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3295   } else {
3296     PetscInt off;
3297     if (PetscDefined(USE_DEBUG)) {
3298       PetscInt dof;
3299       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3300       if (dof) {
3301         if (cone) PetscValidPointer(cone, 3);
3302         if (ornt) PetscValidPointer(ornt, 4);
3303       }
3304     }
3305     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3306     if (cone) *cone = &mesh->cones[off];
3307     if (ornt) *ornt = &mesh->coneOrientations[off];
3308   }
3309   PetscFunctionReturn(PETSC_SUCCESS);
3310 }
3311 
3312 /*@C
3313   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3314 
3315   Not Collective
3316 
3317   Input Parameters:
3318 + dm - The DMPlex
3319 . p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3320 . cone - An array of points which are on the in-edges for point p
3321 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3322         integer giving the prescription for cone traversal.
3323 
3324   Level: beginner
3325 
3326   Notes:
3327   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3328   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3329   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3330   with the identity.
3331 
3332   Fortran Note:
3333   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3334   `DMPlexRestoreCone()` is not needed/available in C.
3335 
3336 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3337 @*/
3338 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3339 {
3340   DM_Plex *mesh = (DM_Plex *)dm->data;
3341 
3342   PetscFunctionBegin;
3343   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3344   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3345   PetscFunctionReturn(PETSC_SUCCESS);
3346 }
3347 
3348 /*@
3349   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3350 
3351   Not Collective
3352 
3353   Input Parameters:
3354 + mesh - The `DMPLEX`
3355 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3356 
3357   Output Parameter:
3358 . size - The support size for point `p`
3359 
3360   Level: beginner
3361 
3362 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3363 @*/
3364 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3365 {
3366   DM_Plex *mesh = (DM_Plex *)dm->data;
3367 
3368   PetscFunctionBegin;
3369   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3370   PetscValidIntPointer(size, 3);
3371   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3372   PetscFunctionReturn(PETSC_SUCCESS);
3373 }
3374 
3375 /*@
3376   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3377 
3378   Not Collective
3379 
3380   Input Parameters:
3381 + mesh - The `DMPLEX`
3382 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3383 - size - The support size for point `p`
3384 
3385   Level: beginner
3386 
3387   Note:
3388   This should be called after `DMPlexSetChart()`.
3389 
3390 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3391 @*/
3392 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3393 {
3394   DM_Plex *mesh = (DM_Plex *)dm->data;
3395 
3396   PetscFunctionBegin;
3397   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3398   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3399   PetscFunctionReturn(PETSC_SUCCESS);
3400 }
3401 
3402 /*@C
3403   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3404 
3405   Not Collective
3406 
3407   Input Parameters:
3408 + mesh - The `DMPLEX`
3409 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3410 
3411   Output Parameter:
3412 . support - An array of points which are on the out-edges for point `p`
3413 
3414   Level: beginner
3415 
3416   Fortran Note:
3417   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3418   `DMPlexRestoreSupport()` is not needed/available in C.
3419 
3420 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3421 @*/
3422 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3423 {
3424   DM_Plex *mesh = (DM_Plex *)dm->data;
3425   PetscInt off;
3426 
3427   PetscFunctionBegin;
3428   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3429   PetscValidPointer(support, 3);
3430   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3431   *support = &mesh->supports[off];
3432   PetscFunctionReturn(PETSC_SUCCESS);
3433 }
3434 
3435 /*@
3436   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3437 
3438   Not Collective
3439 
3440   Input Parameters:
3441 + mesh - The `DMPLEX`
3442 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3443 - support - An array of points which are on the out-edges for point `p`
3444 
3445   Level: beginner
3446 
3447   Note:
3448   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3449 
3450 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3451 @*/
3452 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3453 {
3454   DM_Plex *mesh = (DM_Plex *)dm->data;
3455   PetscInt pStart, pEnd;
3456   PetscInt dof, off, c;
3457 
3458   PetscFunctionBegin;
3459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3460   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3461   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3462   if (dof) PetscValidIntPointer(support, 3);
3463   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3464   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);
3465   for (c = 0; c < dof; ++c) {
3466     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);
3467     mesh->supports[off + c] = support[c];
3468   }
3469   PetscFunctionReturn(PETSC_SUCCESS);
3470 }
3471 
3472 /*@
3473   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3474 
3475   Not Collective
3476 
3477   Input Parameters:
3478 + mesh - The `DMPLEX`
3479 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3480 . supportPos - The local index in the cone where the point should be put
3481 - supportPoint - The mesh point to insert
3482 
3483   Level: beginner
3484 
3485 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3486 @*/
3487 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3488 {
3489   DM_Plex *mesh = (DM_Plex *)dm->data;
3490   PetscInt pStart, pEnd;
3491   PetscInt dof, off;
3492 
3493   PetscFunctionBegin;
3494   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3495   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3496   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3497   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3498   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);
3499   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);
3500   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);
3501   mesh->supports[off + supportPos] = supportPoint;
3502   PetscFunctionReturn(PETSC_SUCCESS);
3503 }
3504 
3505 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3506 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3507 {
3508   switch (ct) {
3509   case DM_POLYTOPE_SEGMENT:
3510     if (o == -1) return -2;
3511     break;
3512   case DM_POLYTOPE_TRIANGLE:
3513     if (o == -3) return -1;
3514     if (o == -2) return -3;
3515     if (o == -1) return -2;
3516     break;
3517   case DM_POLYTOPE_QUADRILATERAL:
3518     if (o == -4) return -2;
3519     if (o == -3) return -1;
3520     if (o == -2) return -4;
3521     if (o == -1) return -3;
3522     break;
3523   default:
3524     return o;
3525   }
3526   return o;
3527 }
3528 
3529 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3530 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3531 {
3532   switch (ct) {
3533   case DM_POLYTOPE_SEGMENT:
3534     if ((o == -2) || (o == 1)) return -1;
3535     if (o == -1) return 0;
3536     break;
3537   case DM_POLYTOPE_TRIANGLE:
3538     if (o == -3) return -2;
3539     if (o == -2) return -1;
3540     if (o == -1) return -3;
3541     break;
3542   case DM_POLYTOPE_QUADRILATERAL:
3543     if (o == -4) return -2;
3544     if (o == -3) return -1;
3545     if (o == -2) return -4;
3546     if (o == -1) return -3;
3547     break;
3548   default:
3549     return o;
3550   }
3551   return o;
3552 }
3553 
3554 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3555 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3556 {
3557   PetscInt pStart, pEnd, p;
3558 
3559   PetscFunctionBegin;
3560   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3561   for (p = pStart; p < pEnd; ++p) {
3562     const PetscInt *cone, *ornt;
3563     PetscInt        coneSize, c;
3564 
3565     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3566     PetscCall(DMPlexGetCone(dm, p, &cone));
3567     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3568     for (c = 0; c < coneSize; ++c) {
3569       DMPolytopeType ct;
3570       const PetscInt o = ornt[c];
3571 
3572       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3573       switch (ct) {
3574       case DM_POLYTOPE_SEGMENT:
3575         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3576         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3577         break;
3578       case DM_POLYTOPE_TRIANGLE:
3579         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3580         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3581         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3582         break;
3583       case DM_POLYTOPE_QUADRILATERAL:
3584         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3585         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3586         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3587         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3588         break;
3589       default:
3590         break;
3591       }
3592     }
3593   }
3594   PetscFunctionReturn(PETSC_SUCCESS);
3595 }
3596 
3597 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3598 {
3599   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3600   PetscInt       *closure;
3601   const PetscInt *tmp = NULL, *tmpO = NULL;
3602   PetscInt        off = 0, tmpSize, t;
3603 
3604   PetscFunctionBeginHot;
3605   if (ornt) {
3606     PetscCall(DMPlexGetCellType(dm, p, &ct));
3607     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3608   }
3609   if (*points) {
3610     closure = *points;
3611   } else {
3612     PetscInt maxConeSize, maxSupportSize;
3613     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3614     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3615   }
3616   if (useCone) {
3617     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3618     PetscCall(DMPlexGetCone(dm, p, &tmp));
3619     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3620   } else {
3621     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3622     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3623   }
3624   if (ct == DM_POLYTOPE_UNKNOWN) {
3625     closure[off++] = p;
3626     closure[off++] = 0;
3627     for (t = 0; t < tmpSize; ++t) {
3628       closure[off++] = tmp[t];
3629       closure[off++] = tmpO ? tmpO[t] : 0;
3630     }
3631   } else {
3632     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3633 
3634     /* We assume that cells with a valid type have faces with a valid type */
3635     closure[off++] = p;
3636     closure[off++] = ornt;
3637     for (t = 0; t < tmpSize; ++t) {
3638       DMPolytopeType ft;
3639 
3640       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3641       closure[off++] = tmp[arr[t]];
3642       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3643     }
3644   }
3645   if (numPoints) *numPoints = tmpSize + 1;
3646   if (points) *points = closure;
3647   PetscFunctionReturn(PETSC_SUCCESS);
3648 }
3649 
3650 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3651 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3652 {
3653   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3654   const PetscInt *cone, *ornt;
3655   PetscInt       *pts, *closure = NULL;
3656   DMPolytopeType  ft;
3657   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3658   PetscInt        dim, coneSize, c, d, clSize, cl;
3659 
3660   PetscFunctionBeginHot;
3661   PetscCall(DMGetDimension(dm, &dim));
3662   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3663   PetscCall(DMPlexGetCone(dm, point, &cone));
3664   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3665   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3666   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3667   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3668   maxSize       = PetscMax(coneSeries, supportSeries);
3669   if (*points) {
3670     pts = *points;
3671   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3672   c        = 0;
3673   pts[c++] = point;
3674   pts[c++] = o;
3675   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3676   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3677   for (cl = 0; cl < clSize * 2; cl += 2) {
3678     pts[c++] = closure[cl];
3679     pts[c++] = closure[cl + 1];
3680   }
3681   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3682   for (cl = 0; cl < clSize * 2; cl += 2) {
3683     pts[c++] = closure[cl];
3684     pts[c++] = closure[cl + 1];
3685   }
3686   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3687   for (d = 2; d < coneSize; ++d) {
3688     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3689     pts[c++] = cone[arr[d * 2 + 0]];
3690     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3691   }
3692   if (dim >= 3) {
3693     for (d = 2; d < coneSize; ++d) {
3694       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3695       const PetscInt *fcone, *fornt;
3696       PetscInt        fconeSize, fc, i;
3697 
3698       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3699       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3700       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3701       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3702       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3703       for (fc = 0; fc < fconeSize; ++fc) {
3704         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3705         const PetscInt co = farr[fc * 2 + 1];
3706 
3707         for (i = 0; i < c; i += 2)
3708           if (pts[i] == cp) break;
3709         if (i == c) {
3710           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3711           pts[c++] = cp;
3712           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3713         }
3714       }
3715     }
3716   }
3717   *numPoints = c / 2;
3718   *points    = pts;
3719   PetscFunctionReturn(PETSC_SUCCESS);
3720 }
3721 
3722 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3723 {
3724   DMPolytopeType ct;
3725   PetscInt      *closure, *fifo;
3726   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3727   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3728   PetscInt       depth, maxSize;
3729 
3730   PetscFunctionBeginHot;
3731   PetscCall(DMPlexGetDepth(dm, &depth));
3732   if (depth == 1) {
3733     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3734     PetscFunctionReturn(PETSC_SUCCESS);
3735   }
3736   PetscCall(DMPlexGetCellType(dm, p, &ct));
3737   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3738   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3739     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3740     PetscFunctionReturn(PETSC_SUCCESS);
3741   }
3742   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3743   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3744   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3745   maxSize       = PetscMax(coneSeries, supportSeries);
3746   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3747   if (*points) {
3748     closure = *points;
3749   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3750   closure[closureSize++] = p;
3751   closure[closureSize++] = ornt;
3752   fifo[fifoSize++]       = p;
3753   fifo[fifoSize++]       = ornt;
3754   fifo[fifoSize++]       = ct;
3755   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3756   while (fifoSize - fifoStart) {
3757     const PetscInt       q    = fifo[fifoStart++];
3758     const PetscInt       o    = fifo[fifoStart++];
3759     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3760     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3761     const PetscInt      *tmp, *tmpO;
3762     PetscInt             tmpSize, t;
3763 
3764     if (PetscDefined(USE_DEBUG)) {
3765       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3766       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);
3767     }
3768     if (useCone) {
3769       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3770       PetscCall(DMPlexGetCone(dm, q, &tmp));
3771       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3772     } else {
3773       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3774       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3775       tmpO = NULL;
3776     }
3777     for (t = 0; t < tmpSize; ++t) {
3778       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3779       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3780       const PetscInt cp = tmp[ip];
3781       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3782       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3783       PetscInt       c;
3784 
3785       /* Check for duplicate */
3786       for (c = 0; c < closureSize; c += 2) {
3787         if (closure[c] == cp) break;
3788       }
3789       if (c == closureSize) {
3790         closure[closureSize++] = cp;
3791         closure[closureSize++] = co;
3792         fifo[fifoSize++]       = cp;
3793         fifo[fifoSize++]       = co;
3794         fifo[fifoSize++]       = ct;
3795       }
3796     }
3797   }
3798   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3799   if (numPoints) *numPoints = closureSize / 2;
3800   if (points) *points = closure;
3801   PetscFunctionReturn(PETSC_SUCCESS);
3802 }
3803 
3804 /*@C
3805   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3806 
3807   Not Collective
3808 
3809   Input Parameters:
3810 + dm      - The `DMPLEX`
3811 . p       - The mesh point
3812 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3813 
3814   Input/Output Parameter:
3815 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3816            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
3817 
3818   Output Parameter:
3819 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3820 
3821   Level: beginner
3822 
3823   Note:
3824   If using internal storage (points is `NULL` on input), each call overwrites the last output.
3825 
3826   Fortran Note:
3827   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
3828 
3829 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3830 @*/
3831 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3832 {
3833   PetscFunctionBeginHot;
3834   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3835   if (numPoints) PetscValidIntPointer(numPoints, 4);
3836   if (points) PetscValidPointer(points, 5);
3837   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3838   PetscFunctionReturn(PETSC_SUCCESS);
3839 }
3840 
3841 /*@C
3842   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3843 
3844   Not Collective
3845 
3846   Input Parameters:
3847 + dm        - The `DMPLEX`
3848 . p         - The mesh point
3849 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3850 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3851 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3852 
3853   Level: beginner
3854 
3855   Note:
3856   If not using internal storage (points is not `NULL` on input), this call is unnecessary
3857 
3858 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3859 @*/
3860 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3861 {
3862   PetscFunctionBeginHot;
3863   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3864   if (numPoints) *numPoints = 0;
3865   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3866   PetscFunctionReturn(PETSC_SUCCESS);
3867 }
3868 
3869 /*@
3870   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3871 
3872   Not Collective
3873 
3874   Input Parameter:
3875 . mesh - The `DMPLEX`
3876 
3877   Output Parameters:
3878 + maxConeSize - The maximum number of in-edges
3879 - maxSupportSize - The maximum number of out-edges
3880 
3881   Level: beginner
3882 
3883 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3884 @*/
3885 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3886 {
3887   DM_Plex *mesh = (DM_Plex *)dm->data;
3888 
3889   PetscFunctionBegin;
3890   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3891   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3892   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3893   PetscFunctionReturn(PETSC_SUCCESS);
3894 }
3895 
3896 PetscErrorCode DMSetUp_Plex(DM dm)
3897 {
3898   DM_Plex *mesh = (DM_Plex *)dm->data;
3899   PetscInt size, maxSupportSize;
3900 
3901   PetscFunctionBegin;
3902   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3903   PetscCall(PetscSectionSetUp(mesh->coneSection));
3904   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3905   PetscCall(PetscMalloc1(size, &mesh->cones));
3906   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3907   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3908   if (maxSupportSize) {
3909     PetscCall(PetscSectionSetUp(mesh->supportSection));
3910     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3911     PetscCall(PetscMalloc1(size, &mesh->supports));
3912   }
3913   PetscFunctionReturn(PETSC_SUCCESS);
3914 }
3915 
3916 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3917 {
3918   PetscFunctionBegin;
3919   if (subdm) PetscCall(DMClone(dm, subdm));
3920   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3921   if (subdm) (*subdm)->useNatural = dm->useNatural;
3922   if (dm->useNatural && dm->sfMigration) {
3923     PetscSF sfNatural;
3924 
3925     (*subdm)->sfMigration = dm->sfMigration;
3926     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3927     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3928     (*subdm)->sfNatural = sfNatural;
3929   }
3930   PetscFunctionReturn(PETSC_SUCCESS);
3931 }
3932 
3933 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3934 {
3935   PetscInt i = 0;
3936 
3937   PetscFunctionBegin;
3938   PetscCall(DMClone(dms[0], superdm));
3939   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3940   (*superdm)->useNatural = PETSC_FALSE;
3941   for (i = 0; i < len; i++) {
3942     if (dms[i]->useNatural && dms[i]->sfMigration) {
3943       PetscSF sfNatural;
3944 
3945       (*superdm)->sfMigration = dms[i]->sfMigration;
3946       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3947       (*superdm)->useNatural = PETSC_TRUE;
3948       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3949       (*superdm)->sfNatural = sfNatural;
3950       break;
3951     }
3952   }
3953   PetscFunctionReturn(PETSC_SUCCESS);
3954 }
3955 
3956 /*@
3957   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3958 
3959   Not Collective
3960 
3961   Input Parameter:
3962 . mesh - The `DMPLEX`
3963 
3964   Level: beginner
3965 
3966   Note:
3967   This should be called after all calls to `DMPlexSetCone()`
3968 
3969 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3970 @*/
3971 PetscErrorCode DMPlexSymmetrize(DM dm)
3972 {
3973   DM_Plex  *mesh = (DM_Plex *)dm->data;
3974   PetscInt *offsets;
3975   PetscInt  supportSize;
3976   PetscInt  pStart, pEnd, p;
3977 
3978   PetscFunctionBegin;
3979   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3980   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3981   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3982   /* Calculate support sizes */
3983   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3984   for (p = pStart; p < pEnd; ++p) {
3985     PetscInt dof, off, c;
3986 
3987     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3988     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3989     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3990   }
3991   PetscCall(PetscSectionSetUp(mesh->supportSection));
3992   /* Calculate supports */
3993   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3994   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3995   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3996   for (p = pStart; p < pEnd; ++p) {
3997     PetscInt dof, off, c;
3998 
3999     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4000     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4001     for (c = off; c < off + dof; ++c) {
4002       const PetscInt q = mesh->cones[c];
4003       PetscInt       offS;
4004 
4005       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4006 
4007       mesh->supports[offS + offsets[q]] = p;
4008       ++offsets[q];
4009     }
4010   }
4011   PetscCall(PetscFree(offsets));
4012   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4013   PetscFunctionReturn(PETSC_SUCCESS);
4014 }
4015 
4016 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4017 {
4018   IS stratumIS;
4019 
4020   PetscFunctionBegin;
4021   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4022   if (PetscDefined(USE_DEBUG)) {
4023     PetscInt  qStart, qEnd, numLevels, level;
4024     PetscBool overlap = PETSC_FALSE;
4025     PetscCall(DMLabelGetNumValues(label, &numLevels));
4026     for (level = 0; level < numLevels; level++) {
4027       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4028       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4029         overlap = PETSC_TRUE;
4030         break;
4031       }
4032     }
4033     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);
4034   }
4035   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4036   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4037   PetscCall(ISDestroy(&stratumIS));
4038   PetscFunctionReturn(PETSC_SUCCESS);
4039 }
4040 
4041 /*@
4042   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4043   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4044   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4045   the DAG.
4046 
4047   Collective
4048 
4049   Input Parameter:
4050 . mesh - The `DMPLEX`
4051 
4052   Level: beginner
4053 
4054   Notes:
4055   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4056   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4057   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4058   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4059   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4060 
4061   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4062   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4063   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
4064   to interpolate only that one (e0), so that
4065 .vb
4066   cone(c0) = {e0, v2}
4067   cone(e0) = {v0, v1}
4068 .ve
4069   If `DMPlexStratify()` is run on this mesh, it will give depths
4070 .vb
4071    depth 0 = {v0, v1, v2}
4072    depth 1 = {e0, c0}
4073 .ve
4074   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4075 
4076   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4077 
4078 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4079 @*/
4080 PetscErrorCode DMPlexStratify(DM dm)
4081 {
4082   DM_Plex *mesh = (DM_Plex *)dm->data;
4083   DMLabel  label;
4084   PetscInt pStart, pEnd, p;
4085   PetscInt numRoots = 0, numLeaves = 0;
4086 
4087   PetscFunctionBegin;
4088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4089   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4090 
4091   /* Create depth label */
4092   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4093   PetscCall(DMCreateLabel(dm, "depth"));
4094   PetscCall(DMPlexGetDepthLabel(dm, &label));
4095 
4096   {
4097     /* Initialize roots and count leaves */
4098     PetscInt sMin = PETSC_MAX_INT;
4099     PetscInt sMax = PETSC_MIN_INT;
4100     PetscInt coneSize, supportSize;
4101 
4102     for (p = pStart; p < pEnd; ++p) {
4103       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4104       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4105       if (!coneSize && supportSize) {
4106         sMin = PetscMin(p, sMin);
4107         sMax = PetscMax(p, sMax);
4108         ++numRoots;
4109       } else if (!supportSize && coneSize) {
4110         ++numLeaves;
4111       } else if (!supportSize && !coneSize) {
4112         /* Isolated points */
4113         sMin = PetscMin(p, sMin);
4114         sMax = PetscMax(p, sMax);
4115       }
4116     }
4117     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4118   }
4119 
4120   if (numRoots + numLeaves == (pEnd - pStart)) {
4121     PetscInt sMin = PETSC_MAX_INT;
4122     PetscInt sMax = PETSC_MIN_INT;
4123     PetscInt coneSize, supportSize;
4124 
4125     for (p = pStart; p < pEnd; ++p) {
4126       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4127       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4128       if (!supportSize && coneSize) {
4129         sMin = PetscMin(p, sMin);
4130         sMax = PetscMax(p, sMax);
4131       }
4132     }
4133     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4134   } else {
4135     PetscInt level = 0;
4136     PetscInt qStart, qEnd, q;
4137 
4138     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4139     while (qEnd > qStart) {
4140       PetscInt sMin = PETSC_MAX_INT;
4141       PetscInt sMax = PETSC_MIN_INT;
4142 
4143       for (q = qStart; q < qEnd; ++q) {
4144         const PetscInt *support;
4145         PetscInt        supportSize, s;
4146 
4147         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4148         PetscCall(DMPlexGetSupport(dm, q, &support));
4149         for (s = 0; s < supportSize; ++s) {
4150           sMin = PetscMin(support[s], sMin);
4151           sMax = PetscMax(support[s], sMax);
4152         }
4153       }
4154       PetscCall(DMLabelGetNumValues(label, &level));
4155       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4156       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4157     }
4158   }
4159   { /* just in case there is an empty process */
4160     PetscInt numValues, maxValues = 0, v;
4161 
4162     PetscCall(DMLabelGetNumValues(label, &numValues));
4163     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4164     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4165   }
4166   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4167   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4168   PetscFunctionReturn(PETSC_SUCCESS);
4169 }
4170 
4171 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4172 {
4173   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4174   PetscInt       dim, depth, pheight, coneSize;
4175 
4176   PetscFunctionBeginHot;
4177   PetscCall(DMGetDimension(dm, &dim));
4178   PetscCall(DMPlexGetDepth(dm, &depth));
4179   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4180   pheight = depth - pdepth;
4181   if (depth <= 1) {
4182     switch (pdepth) {
4183     case 0:
4184       ct = DM_POLYTOPE_POINT;
4185       break;
4186     case 1:
4187       switch (coneSize) {
4188       case 2:
4189         ct = DM_POLYTOPE_SEGMENT;
4190         break;
4191       case 3:
4192         ct = DM_POLYTOPE_TRIANGLE;
4193         break;
4194       case 4:
4195         switch (dim) {
4196         case 2:
4197           ct = DM_POLYTOPE_QUADRILATERAL;
4198           break;
4199         case 3:
4200           ct = DM_POLYTOPE_TETRAHEDRON;
4201           break;
4202         default:
4203           break;
4204         }
4205         break;
4206       case 5:
4207         ct = DM_POLYTOPE_PYRAMID;
4208         break;
4209       case 6:
4210         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4211         break;
4212       case 8:
4213         ct = DM_POLYTOPE_HEXAHEDRON;
4214         break;
4215       default:
4216         break;
4217       }
4218     }
4219   } else {
4220     if (pdepth == 0) {
4221       ct = DM_POLYTOPE_POINT;
4222     } else if (pheight == 0) {
4223       switch (dim) {
4224       case 1:
4225         switch (coneSize) {
4226         case 2:
4227           ct = DM_POLYTOPE_SEGMENT;
4228           break;
4229         default:
4230           break;
4231         }
4232         break;
4233       case 2:
4234         switch (coneSize) {
4235         case 3:
4236           ct = DM_POLYTOPE_TRIANGLE;
4237           break;
4238         case 4:
4239           ct = DM_POLYTOPE_QUADRILATERAL;
4240           break;
4241         default:
4242           break;
4243         }
4244         break;
4245       case 3:
4246         switch (coneSize) {
4247         case 4:
4248           ct = DM_POLYTOPE_TETRAHEDRON;
4249           break;
4250         case 5: {
4251           const PetscInt *cone;
4252           PetscInt        faceConeSize;
4253 
4254           PetscCall(DMPlexGetCone(dm, p, &cone));
4255           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4256           switch (faceConeSize) {
4257           case 3:
4258             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4259             break;
4260           case 4:
4261             ct = DM_POLYTOPE_PYRAMID;
4262             break;
4263           }
4264         } break;
4265         case 6:
4266           ct = DM_POLYTOPE_HEXAHEDRON;
4267           break;
4268         default:
4269           break;
4270         }
4271         break;
4272       default:
4273         break;
4274       }
4275     } else if (pheight > 0) {
4276       switch (coneSize) {
4277       case 2:
4278         ct = DM_POLYTOPE_SEGMENT;
4279         break;
4280       case 3:
4281         ct = DM_POLYTOPE_TRIANGLE;
4282         break;
4283       case 4:
4284         ct = DM_POLYTOPE_QUADRILATERAL;
4285         break;
4286       default:
4287         break;
4288       }
4289     }
4290   }
4291   *pt = ct;
4292   PetscFunctionReturn(PETSC_SUCCESS);
4293 }
4294 
4295 /*@
4296   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4297 
4298   Collective
4299 
4300   Input Parameter:
4301 . mesh - The `DMPLEX`
4302 
4303   Level: developer
4304 
4305   Note:
4306   This function is normally called automatically when a cell type is requested. It creates an
4307   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4308   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4309 
4310   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4311 
4312 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4313 @*/
4314 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4315 {
4316   DM_Plex *mesh;
4317   DMLabel  ctLabel;
4318   PetscInt pStart, pEnd, p;
4319 
4320   PetscFunctionBegin;
4321   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4322   mesh = (DM_Plex *)dm->data;
4323   PetscCall(DMCreateLabel(dm, "celltype"));
4324   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4325   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4326   for (p = pStart; p < pEnd; ++p) {
4327     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4328     PetscInt       pdepth;
4329 
4330     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4331     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4332     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4333     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4334   }
4335   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4336   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4337   PetscFunctionReturn(PETSC_SUCCESS);
4338 }
4339 
4340 /*@C
4341   DMPlexGetJoin - Get an array for the join of the set of points
4342 
4343   Not Collective
4344 
4345   Input Parameters:
4346 + dm - The `DMPLEX` object
4347 . numPoints - The number of input points for the join
4348 - points - The input points
4349 
4350   Output Parameters:
4351 + numCoveredPoints - The number of points in the join
4352 - coveredPoints - The points in the join
4353 
4354   Level: intermediate
4355 
4356   Note:
4357   Currently, this is restricted to a single level join
4358 
4359   Fortran Note:
4360   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4361 
4362 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4363 @*/
4364 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4365 {
4366   DM_Plex  *mesh = (DM_Plex *)dm->data;
4367   PetscInt *join[2];
4368   PetscInt  joinSize, i = 0;
4369   PetscInt  dof, off, p, c, m;
4370   PetscInt  maxSupportSize;
4371 
4372   PetscFunctionBegin;
4373   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4374   PetscValidIntPointer(points, 3);
4375   PetscValidIntPointer(numCoveredPoints, 4);
4376   PetscValidPointer(coveredPoints, 5);
4377   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4378   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4379   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4380   /* Copy in support of first point */
4381   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4382   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4383   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4384   /* Check each successive support */
4385   for (p = 1; p < numPoints; ++p) {
4386     PetscInt newJoinSize = 0;
4387 
4388     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4389     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4390     for (c = 0; c < dof; ++c) {
4391       const PetscInt point = mesh->supports[off + c];
4392 
4393       for (m = 0; m < joinSize; ++m) {
4394         if (point == join[i][m]) {
4395           join[1 - i][newJoinSize++] = point;
4396           break;
4397         }
4398       }
4399     }
4400     joinSize = newJoinSize;
4401     i        = 1 - i;
4402   }
4403   *numCoveredPoints = joinSize;
4404   *coveredPoints    = join[i];
4405   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4406   PetscFunctionReturn(PETSC_SUCCESS);
4407 }
4408 
4409 /*@C
4410   DMPlexRestoreJoin - Restore an array for the join of the set of points
4411 
4412   Not Collective
4413 
4414   Input Parameters:
4415 + dm - The `DMPLEX` object
4416 . numPoints - The number of input points for the join
4417 - points - The input points
4418 
4419   Output Parameters:
4420 + numCoveredPoints - The number of points in the join
4421 - coveredPoints - The points in the join
4422 
4423   Level: intermediate
4424 
4425   Fortran Note:
4426   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4427 
4428 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4429 @*/
4430 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4431 {
4432   PetscFunctionBegin;
4433   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4434   if (points) PetscValidIntPointer(points, 3);
4435   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4436   PetscValidPointer(coveredPoints, 5);
4437   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4438   if (numCoveredPoints) *numCoveredPoints = 0;
4439   PetscFunctionReturn(PETSC_SUCCESS);
4440 }
4441 
4442 /*@C
4443   DMPlexGetFullJoin - Get an array for the join of the set of points
4444 
4445   Not Collective
4446 
4447   Input Parameters:
4448 + dm - The `DMPLEX` object
4449 . numPoints - The number of input points for the join
4450 - points - The input points
4451 
4452   Output Parameters:
4453 + numCoveredPoints - The number of points in the join
4454 - coveredPoints - The points in the join
4455 
4456   Level: intermediate
4457 
4458   Fortran Note:
4459   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4460 
4461 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4462 @*/
4463 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4464 {
4465   PetscInt *offsets, **closures;
4466   PetscInt *join[2];
4467   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4468   PetscInt  p, d, c, m, ms;
4469 
4470   PetscFunctionBegin;
4471   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4472   PetscValidIntPointer(points, 3);
4473   PetscValidIntPointer(numCoveredPoints, 4);
4474   PetscValidPointer(coveredPoints, 5);
4475 
4476   PetscCall(DMPlexGetDepth(dm, &depth));
4477   PetscCall(PetscCalloc1(numPoints, &closures));
4478   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4479   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4480   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4481   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4482   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4483 
4484   for (p = 0; p < numPoints; ++p) {
4485     PetscInt closureSize;
4486 
4487     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4488 
4489     offsets[p * (depth + 2) + 0] = 0;
4490     for (d = 0; d < depth + 1; ++d) {
4491       PetscInt pStart, pEnd, i;
4492 
4493       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4494       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4495         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4496           offsets[p * (depth + 2) + d + 1] = i;
4497           break;
4498         }
4499       }
4500       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4501     }
4502     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);
4503   }
4504   for (d = 0; d < depth + 1; ++d) {
4505     PetscInt dof;
4506 
4507     /* Copy in support of first point */
4508     dof = offsets[d + 1] - offsets[d];
4509     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4510     /* Check each successive cone */
4511     for (p = 1; p < numPoints && joinSize; ++p) {
4512       PetscInt newJoinSize = 0;
4513 
4514       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4515       for (c = 0; c < dof; ++c) {
4516         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4517 
4518         for (m = 0; m < joinSize; ++m) {
4519           if (point == join[i][m]) {
4520             join[1 - i][newJoinSize++] = point;
4521             break;
4522           }
4523         }
4524       }
4525       joinSize = newJoinSize;
4526       i        = 1 - i;
4527     }
4528     if (joinSize) break;
4529   }
4530   *numCoveredPoints = joinSize;
4531   *coveredPoints    = join[i];
4532   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4533   PetscCall(PetscFree(closures));
4534   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4535   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4536   PetscFunctionReturn(PETSC_SUCCESS);
4537 }
4538 
4539 /*@C
4540   DMPlexGetMeet - Get an array for the meet of the set of points
4541 
4542   Not Collective
4543 
4544   Input Parameters:
4545 + dm - The `DMPLEX` object
4546 . numPoints - The number of input points for the meet
4547 - points - The input points
4548 
4549   Output Parameters:
4550 + numCoveredPoints - The number of points in the meet
4551 - coveredPoints - The points in the meet
4552 
4553   Level: intermediate
4554 
4555   Note:
4556   Currently, this is restricted to a single level meet
4557 
4558   Fortran Notes:
4559   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4560 
4561 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4562 @*/
4563 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4564 {
4565   DM_Plex  *mesh = (DM_Plex *)dm->data;
4566   PetscInt *meet[2];
4567   PetscInt  meetSize, i = 0;
4568   PetscInt  dof, off, p, c, m;
4569   PetscInt  maxConeSize;
4570 
4571   PetscFunctionBegin;
4572   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4573   PetscValidIntPointer(points, 3);
4574   PetscValidIntPointer(numCoveringPoints, 4);
4575   PetscValidPointer(coveringPoints, 5);
4576   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4577   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4578   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4579   /* Copy in cone of first point */
4580   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4581   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4582   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4583   /* Check each successive cone */
4584   for (p = 1; p < numPoints; ++p) {
4585     PetscInt newMeetSize = 0;
4586 
4587     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4588     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4589     for (c = 0; c < dof; ++c) {
4590       const PetscInt point = mesh->cones[off + c];
4591 
4592       for (m = 0; m < meetSize; ++m) {
4593         if (point == meet[i][m]) {
4594           meet[1 - i][newMeetSize++] = point;
4595           break;
4596         }
4597       }
4598     }
4599     meetSize = newMeetSize;
4600     i        = 1 - i;
4601   }
4602   *numCoveringPoints = meetSize;
4603   *coveringPoints    = meet[i];
4604   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4605   PetscFunctionReturn(PETSC_SUCCESS);
4606 }
4607 
4608 /*@C
4609   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4610 
4611   Not Collective
4612 
4613   Input Parameters:
4614 + dm - The `DMPLEX` object
4615 . numPoints - The number of input points for the meet
4616 - points - The input points
4617 
4618   Output Parameters:
4619 + numCoveredPoints - The number of points in the meet
4620 - coveredPoints - The points in the meet
4621 
4622   Level: intermediate
4623 
4624   Fortran Note:
4625   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4626 
4627 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4628 @*/
4629 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4630 {
4631   PetscFunctionBegin;
4632   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4633   if (points) PetscValidIntPointer(points, 3);
4634   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4635   PetscValidPointer(coveredPoints, 5);
4636   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4637   if (numCoveredPoints) *numCoveredPoints = 0;
4638   PetscFunctionReturn(PETSC_SUCCESS);
4639 }
4640 
4641 /*@C
4642   DMPlexGetFullMeet - Get an array for the meet of the set of points
4643 
4644   Not Collective
4645 
4646   Input Parameters:
4647 + dm - The `DMPLEX` object
4648 . numPoints - The number of input points for the meet
4649 - points - The input points
4650 
4651   Output Parameters:
4652 + numCoveredPoints - The number of points in the meet
4653 - coveredPoints - The points in the meet
4654 
4655   Level: intermediate
4656 
4657   Fortran Note:
4658   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4659 
4660 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4661 @*/
4662 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4663 {
4664   PetscInt *offsets, **closures;
4665   PetscInt *meet[2];
4666   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4667   PetscInt  p, h, c, m, mc;
4668 
4669   PetscFunctionBegin;
4670   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4671   PetscValidIntPointer(points, 3);
4672   PetscValidIntPointer(numCoveredPoints, 4);
4673   PetscValidPointer(coveredPoints, 5);
4674 
4675   PetscCall(DMPlexGetDepth(dm, &height));
4676   PetscCall(PetscMalloc1(numPoints, &closures));
4677   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4678   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4679   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4680   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4681   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4682 
4683   for (p = 0; p < numPoints; ++p) {
4684     PetscInt closureSize;
4685 
4686     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4687 
4688     offsets[p * (height + 2) + 0] = 0;
4689     for (h = 0; h < height + 1; ++h) {
4690       PetscInt pStart, pEnd, i;
4691 
4692       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4693       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4694         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4695           offsets[p * (height + 2) + h + 1] = i;
4696           break;
4697         }
4698       }
4699       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4700     }
4701     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);
4702   }
4703   for (h = 0; h < height + 1; ++h) {
4704     PetscInt dof;
4705 
4706     /* Copy in cone of first point */
4707     dof = offsets[h + 1] - offsets[h];
4708     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4709     /* Check each successive cone */
4710     for (p = 1; p < numPoints && meetSize; ++p) {
4711       PetscInt newMeetSize = 0;
4712 
4713       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4714       for (c = 0; c < dof; ++c) {
4715         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4716 
4717         for (m = 0; m < meetSize; ++m) {
4718           if (point == meet[i][m]) {
4719             meet[1 - i][newMeetSize++] = point;
4720             break;
4721           }
4722         }
4723       }
4724       meetSize = newMeetSize;
4725       i        = 1 - i;
4726     }
4727     if (meetSize) break;
4728   }
4729   *numCoveredPoints = meetSize;
4730   *coveredPoints    = meet[i];
4731   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4732   PetscCall(PetscFree(closures));
4733   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4734   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4735   PetscFunctionReturn(PETSC_SUCCESS);
4736 }
4737 
4738 /*@C
4739   DMPlexEqual - Determine if two `DM` have the same topology
4740 
4741   Not Collective
4742 
4743   Input Parameters:
4744 + dmA - A `DMPLEX` object
4745 - dmB - A `DMPLEX` object
4746 
4747   Output Parameter:
4748 . equal - `PETSC_TRUE` if the topologies are identical
4749 
4750   Level: intermediate
4751 
4752   Note:
4753   We are not solving graph isomorphism, so we do not permute.
4754 
4755 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4756 @*/
4757 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4758 {
4759   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4760 
4761   PetscFunctionBegin;
4762   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4763   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4764   PetscValidBoolPointer(equal, 3);
4765 
4766   *equal = PETSC_FALSE;
4767   PetscCall(DMPlexGetDepth(dmA, &depth));
4768   PetscCall(DMPlexGetDepth(dmB, &depthB));
4769   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4770   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4771   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4772   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4773   for (p = pStart; p < pEnd; ++p) {
4774     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4775     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4776 
4777     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4778     PetscCall(DMPlexGetCone(dmA, p, &cone));
4779     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4780     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4781     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4782     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4783     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4784     for (c = 0; c < coneSize; ++c) {
4785       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4786       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4787     }
4788     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4789     PetscCall(DMPlexGetSupport(dmA, p, &support));
4790     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4791     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4792     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4793     for (s = 0; s < supportSize; ++s) {
4794       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4795     }
4796   }
4797   *equal = PETSC_TRUE;
4798   PetscFunctionReturn(PETSC_SUCCESS);
4799 }
4800 
4801 /*@C
4802   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4803 
4804   Not Collective
4805 
4806   Input Parameters:
4807 + dm         - The `DMPLEX`
4808 . cellDim    - The cell dimension
4809 - numCorners - The number of vertices on a cell
4810 
4811   Output Parameter:
4812 . numFaceVertices - The number of vertices on a face
4813 
4814   Level: developer
4815 
4816   Note:
4817   Of course this can only work for a restricted set of symmetric shapes
4818 
4819 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4820 @*/
4821 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4822 {
4823   MPI_Comm comm;
4824 
4825   PetscFunctionBegin;
4826   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4827   PetscValidIntPointer(numFaceVertices, 4);
4828   switch (cellDim) {
4829   case 0:
4830     *numFaceVertices = 0;
4831     break;
4832   case 1:
4833     *numFaceVertices = 1;
4834     break;
4835   case 2:
4836     switch (numCorners) {
4837     case 3:                 /* triangle */
4838       *numFaceVertices = 2; /* Edge has 2 vertices */
4839       break;
4840     case 4:                 /* quadrilateral */
4841       *numFaceVertices = 2; /* Edge has 2 vertices */
4842       break;
4843     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4844       *numFaceVertices = 3; /* Edge has 3 vertices */
4845       break;
4846     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4847       *numFaceVertices = 3; /* Edge has 3 vertices */
4848       break;
4849     default:
4850       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4851     }
4852     break;
4853   case 3:
4854     switch (numCorners) {
4855     case 4:                 /* tetradehdron */
4856       *numFaceVertices = 3; /* Face has 3 vertices */
4857       break;
4858     case 6:                 /* tet cohesive cells */
4859       *numFaceVertices = 4; /* Face has 4 vertices */
4860       break;
4861     case 8:                 /* hexahedron */
4862       *numFaceVertices = 4; /* Face has 4 vertices */
4863       break;
4864     case 9:                 /* tet cohesive Lagrange cells */
4865       *numFaceVertices = 6; /* Face has 6 vertices */
4866       break;
4867     case 10:                /* quadratic tetrahedron */
4868       *numFaceVertices = 6; /* Face has 6 vertices */
4869       break;
4870     case 12:                /* hex cohesive Lagrange cells */
4871       *numFaceVertices = 6; /* Face has 6 vertices */
4872       break;
4873     case 18:                /* quadratic tet cohesive Lagrange cells */
4874       *numFaceVertices = 6; /* Face has 6 vertices */
4875       break;
4876     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4877       *numFaceVertices = 9; /* Face has 9 vertices */
4878       break;
4879     default:
4880       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4881     }
4882     break;
4883   default:
4884     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4885   }
4886   PetscFunctionReturn(PETSC_SUCCESS);
4887 }
4888 
4889 /*@
4890   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4891 
4892   Not Collective
4893 
4894   Input Parameter:
4895 . dm    - The `DMPLEX` object
4896 
4897   Output Parameter:
4898 . depthLabel - The `DMLabel` recording point depth
4899 
4900   Level: developer
4901 
4902 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4903 @*/
4904 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4905 {
4906   PetscFunctionBegin;
4907   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4908   PetscValidPointer(depthLabel, 2);
4909   *depthLabel = dm->depthLabel;
4910   PetscFunctionReturn(PETSC_SUCCESS);
4911 }
4912 
4913 /*@
4914   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4915 
4916   Not Collective
4917 
4918   Input Parameter:
4919 . dm    - The `DMPLEX` object
4920 
4921   Output Parameter:
4922 . depth - The number of strata (breadth first levels) in the DAG
4923 
4924   Level: developer
4925 
4926   Notes:
4927   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4928 
4929   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4930 
4931   An empty mesh gives -1.
4932 
4933 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4934 @*/
4935 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4936 {
4937   DM_Plex *mesh = (DM_Plex *)dm->data;
4938   DMLabel  label;
4939   PetscInt d = 0;
4940 
4941   PetscFunctionBegin;
4942   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4943   PetscValidIntPointer(depth, 2);
4944   if (mesh->tr) {
4945     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4946   } else {
4947     PetscCall(DMPlexGetDepthLabel(dm, &label));
4948     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4949     *depth = d - 1;
4950   }
4951   PetscFunctionReturn(PETSC_SUCCESS);
4952 }
4953 
4954 /*@
4955   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
4956 
4957   Not Collective
4958 
4959   Input Parameters:
4960 + dm    - The `DMPLEX` object
4961 - depth - The requested depth
4962 
4963   Output Parameters:
4964 + start - The first point at this `depth`
4965 - end   - One beyond the last point at this `depth`
4966 
4967   Level: developer
4968 
4969   Notes:
4970   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4971   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
4972   higher dimension, e.g., "edges".
4973 
4974 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4975 @*/
4976 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4977 {
4978   DM_Plex *mesh = (DM_Plex *)dm->data;
4979   DMLabel  label;
4980   PetscInt pStart, pEnd;
4981 
4982   PetscFunctionBegin;
4983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4984   if (start) {
4985     PetscValidIntPointer(start, 3);
4986     *start = 0;
4987   }
4988   if (end) {
4989     PetscValidIntPointer(end, 4);
4990     *end = 0;
4991   }
4992   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4993   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4994   if (depth < 0) {
4995     if (start) *start = pStart;
4996     if (end) *end = pEnd;
4997     PetscFunctionReturn(PETSC_SUCCESS);
4998   }
4999   if (mesh->tr) {
5000     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5001   } else {
5002     PetscCall(DMPlexGetDepthLabel(dm, &label));
5003     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5004     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5005   }
5006   PetscFunctionReturn(PETSC_SUCCESS);
5007 }
5008 
5009 /*@
5010   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5011 
5012   Not Collective
5013 
5014   Input Parameters:
5015 + dm     - The `DMPLEX` object
5016 - height - The requested height
5017 
5018   Output Parameters:
5019 + start - The first point at this `height`
5020 - end   - One beyond the last point at this `height`
5021 
5022   Level: developer
5023 
5024   Notes:
5025   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5026   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5027   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5028 
5029 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5030 @*/
5031 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5032 {
5033   DMLabel  label;
5034   PetscInt depth, pStart, pEnd;
5035 
5036   PetscFunctionBegin;
5037   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5038   if (start) {
5039     PetscValidIntPointer(start, 3);
5040     *start = 0;
5041   }
5042   if (end) {
5043     PetscValidIntPointer(end, 4);
5044     *end = 0;
5045   }
5046   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5047   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5048   if (height < 0) {
5049     if (start) *start = pStart;
5050     if (end) *end = pEnd;
5051     PetscFunctionReturn(PETSC_SUCCESS);
5052   }
5053   PetscCall(DMPlexGetDepthLabel(dm, &label));
5054   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5055   PetscCall(DMLabelGetNumValues(label, &depth));
5056   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5057   PetscFunctionReturn(PETSC_SUCCESS);
5058 }
5059 
5060 /*@
5061   DMPlexGetPointDepth - Get the `depth` of a given point
5062 
5063   Not Collective
5064 
5065   Input Parameters:
5066 + dm    - The `DMPLEX` object
5067 - point - The point
5068 
5069   Output Parameter:
5070 . depth - The depth of the `point`
5071 
5072   Level: intermediate
5073 
5074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5075 @*/
5076 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5077 {
5078   PetscFunctionBegin;
5079   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5080   PetscValidIntPointer(depth, 3);
5081   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5082   PetscFunctionReturn(PETSC_SUCCESS);
5083 }
5084 
5085 /*@
5086   DMPlexGetPointHeight - Get the `height` of a given point
5087 
5088   Not Collective
5089 
5090   Input Parameters:
5091 + dm    - The `DMPLEX` object
5092 - point - The point
5093 
5094   Output Parameter:
5095 . height - The height of the `point`
5096 
5097   Level: intermediate
5098 
5099 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5100 @*/
5101 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5102 {
5103   PetscInt n, pDepth;
5104 
5105   PetscFunctionBegin;
5106   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5107   PetscValidIntPointer(height, 3);
5108   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5109   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5110   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5111   PetscFunctionReturn(PETSC_SUCCESS);
5112 }
5113 
5114 /*@
5115   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5116 
5117   Not Collective
5118 
5119   Input Parameter:
5120 . dm - The `DMPLEX` object
5121 
5122   Output Parameter:
5123 . celltypeLabel - The `DMLabel` recording cell polytope type
5124 
5125   Level: developer
5126 
5127   Note:
5128   This function will trigger automatica computation of cell types. This can be disabled by calling
5129   `DMCreateLabel`(dm, "celltype") beforehand.
5130 
5131 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5132 @*/
5133 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5134 {
5135   PetscFunctionBegin;
5136   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5137   PetscValidPointer(celltypeLabel, 2);
5138   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5139   *celltypeLabel = dm->celltypeLabel;
5140   PetscFunctionReturn(PETSC_SUCCESS);
5141 }
5142 
5143 /*@
5144   DMPlexGetCellType - Get the polytope type of a given cell
5145 
5146   Not Collective
5147 
5148   Input Parameters:
5149 + dm   - The `DMPLEX` object
5150 - cell - The cell
5151 
5152   Output Parameter:
5153 . celltype - The polytope type of the cell
5154 
5155   Level: intermediate
5156 
5157 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5158 @*/
5159 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5160 {
5161   DM_Plex *mesh = (DM_Plex *)dm->data;
5162   DMLabel  label;
5163   PetscInt ct;
5164 
5165   PetscFunctionBegin;
5166   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5167   PetscValidPointer(celltype, 3);
5168   if (mesh->tr) {
5169     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5170   } else {
5171     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5172     PetscCall(DMLabelGetValue(label, cell, &ct));
5173     PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5174     *celltype = (DMPolytopeType)ct;
5175   }
5176   PetscFunctionReturn(PETSC_SUCCESS);
5177 }
5178 
5179 /*@
5180   DMPlexSetCellType - Set the polytope type of a given cell
5181 
5182   Not Collective
5183 
5184   Input Parameters:
5185 + dm   - The `DMPLEX` object
5186 . cell - The cell
5187 - celltype - The polytope type of the cell
5188 
5189   Level: advanced
5190 
5191   Note:
5192   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5193   is executed. This function will override the computed type. However, if automatic classification will not succeed
5194   and a user wants to manually specify all types, the classification must be disabled by calling
5195   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5196 
5197 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5198 @*/
5199 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5200 {
5201   DMLabel label;
5202 
5203   PetscFunctionBegin;
5204   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5205   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5206   PetscCall(DMLabelSetValue(label, cell, celltype));
5207   PetscFunctionReturn(PETSC_SUCCESS);
5208 }
5209 
5210 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5211 {
5212   PetscSection section, s;
5213   Mat          m;
5214   PetscInt     maxHeight;
5215   const char  *prefix;
5216 
5217   PetscFunctionBegin;
5218   PetscCall(DMClone(dm, cdm));
5219   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5220   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5221   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5222   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5223   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5224   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5225   PetscCall(DMSetLocalSection(*cdm, section));
5226   PetscCall(PetscSectionDestroy(&section));
5227   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5228   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5229   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5230   PetscCall(PetscSectionDestroy(&s));
5231   PetscCall(MatDestroy(&m));
5232 
5233   PetscCall(DMSetNumFields(*cdm, 1));
5234   PetscCall(DMCreateDS(*cdm));
5235   (*cdm)->cloneOpts = PETSC_TRUE;
5236   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5237   PetscFunctionReturn(PETSC_SUCCESS);
5238 }
5239 
5240 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5241 {
5242   Vec coordsLocal, cellCoordsLocal;
5243   DM  coordsDM, cellCoordsDM;
5244 
5245   PetscFunctionBegin;
5246   *field = NULL;
5247   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5248   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5249   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5250   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5251   if (coordsLocal && coordsDM) {
5252     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5253     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5254   }
5255   PetscFunctionReturn(PETSC_SUCCESS);
5256 }
5257 
5258 /*@C
5259   DMPlexGetConeSection - Return a section which describes the layout of cone data
5260 
5261   Not Collective
5262 
5263   Input Parameter:
5264 . dm        - The `DMPLEX` object
5265 
5266   Output Parameter:
5267 . section - The `PetscSection` object
5268 
5269   Level: developer
5270 
5271 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5272 @*/
5273 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5274 {
5275   DM_Plex *mesh = (DM_Plex *)dm->data;
5276 
5277   PetscFunctionBegin;
5278   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5279   if (section) *section = mesh->coneSection;
5280   PetscFunctionReturn(PETSC_SUCCESS);
5281 }
5282 
5283 /*@C
5284   DMPlexGetSupportSection - Return a section which describes the layout of support data
5285 
5286   Not Collective
5287 
5288   Input Parameter:
5289 . dm        - The `DMPLEX` object
5290 
5291   Output Parameter:
5292 . section - The `PetscSection` object
5293 
5294   Level: developer
5295 
5296 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5297 @*/
5298 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5299 {
5300   DM_Plex *mesh = (DM_Plex *)dm->data;
5301 
5302   PetscFunctionBegin;
5303   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5304   if (section) *section = mesh->supportSection;
5305   PetscFunctionReturn(PETSC_SUCCESS);
5306 }
5307 
5308 /*@C
5309   DMPlexGetCones - Return cone data
5310 
5311   Not Collective
5312 
5313   Input Parameter:
5314 . dm        - The `DMPLEX` object
5315 
5316   Output Parameter:
5317 . cones - The cone for each point
5318 
5319   Level: developer
5320 
5321 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5322 @*/
5323 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5324 {
5325   DM_Plex *mesh = (DM_Plex *)dm->data;
5326 
5327   PetscFunctionBegin;
5328   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5329   if (cones) *cones = mesh->cones;
5330   PetscFunctionReturn(PETSC_SUCCESS);
5331 }
5332 
5333 /*@C
5334   DMPlexGetConeOrientations - Return cone orientation data
5335 
5336   Not Collective
5337 
5338   Input Parameter:
5339 . dm        - The `DMPLEX` object
5340 
5341   Output Parameter:
5342 . coneOrientations - The array of cone orientations for all points
5343 
5344   Level: developer
5345 
5346   Notes:
5347   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5348 
5349   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5350 
5351 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5352 @*/
5353 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5354 {
5355   DM_Plex *mesh = (DM_Plex *)dm->data;
5356 
5357   PetscFunctionBegin;
5358   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5359   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5360   PetscFunctionReturn(PETSC_SUCCESS);
5361 }
5362 
5363 /******************************** FEM Support **********************************/
5364 
5365 /*
5366  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5367  representing a line in the section.
5368 */
5369 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5370 {
5371   PetscFunctionBeginHot;
5372   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5373   if (line < 0) {
5374     *k  = 0;
5375     *Nc = 0;
5376   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5377     *k = 1;
5378   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5379     /* An order k SEM disc has k-1 dofs on an edge */
5380     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5381     *k = *k / *Nc + 1;
5382   }
5383   PetscFunctionReturn(PETSC_SUCCESS);
5384 }
5385 
5386 /*@
5387 
5388   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5389   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5390   section provided (or the section of the `DM`).
5391 
5392   Input Parameters:
5393 + dm      - The `DM`
5394 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5395 - section - The `PetscSection` to reorder, or `NULL` for the default section
5396 
5397   Example:
5398   A typical interpolated single-quad mesh might order points as
5399 .vb
5400   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5401 
5402   v4 -- e6 -- v3
5403   |           |
5404   e7    c0    e8
5405   |           |
5406   v1 -- e5 -- v2
5407 .ve
5408 
5409   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5410   dofs in the order of points, e.g.,
5411 .vb
5412     c0 -> [0,1,2,3]
5413     v1 -> [4]
5414     ...
5415     e5 -> [8, 9]
5416 .ve
5417 
5418   which corresponds to the dofs
5419 .vb
5420     6   10  11  7
5421     13  2   3   15
5422     12  0   1   14
5423     4   8   9   5
5424 .ve
5425 
5426   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5427 .vb
5428   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5429 .ve
5430 
5431   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5432 .vb
5433    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5434 .ve
5435 
5436   Level: developer
5437 
5438   Note:
5439   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5440   degree of the basis.
5441 
5442 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5443 @*/
5444 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5445 {
5446   DMLabel   label;
5447   PetscInt  dim, depth = -1, eStart = -1, Nf;
5448   PetscBool vertexchart;
5449 
5450   PetscFunctionBegin;
5451   PetscCall(DMGetDimension(dm, &dim));
5452   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5453   if (point < 0) {
5454     PetscInt sStart, sEnd;
5455 
5456     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5457     point = sEnd - sStart ? sStart : point;
5458   }
5459   PetscCall(DMPlexGetDepthLabel(dm, &label));
5460   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5461   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5462   if (depth == 1) {
5463     eStart = point;
5464   } else if (depth == dim) {
5465     const PetscInt *cone;
5466 
5467     PetscCall(DMPlexGetCone(dm, point, &cone));
5468     if (dim == 2) eStart = cone[0];
5469     else if (dim == 3) {
5470       const PetscInt *cone2;
5471       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5472       eStart = cone2[0];
5473     } 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);
5474   } 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);
5475   { /* Determine whether the chart covers all points or just vertices. */
5476     PetscInt pStart, pEnd, cStart, cEnd;
5477     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5478     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5479     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5480     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5481     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5482   }
5483   PetscCall(PetscSectionGetNumFields(section, &Nf));
5484   for (PetscInt d = 1; d <= dim; d++) {
5485     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5486     PetscInt *perm;
5487 
5488     for (f = 0; f < Nf; ++f) {
5489       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5490       size += PetscPowInt(k + 1, d) * Nc;
5491     }
5492     PetscCall(PetscMalloc1(size, &perm));
5493     for (f = 0; f < Nf; ++f) {
5494       switch (d) {
5495       case 1:
5496         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5497         /*
5498          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5499          We want              [ vtx0; edge of length k-1; vtx1 ]
5500          */
5501         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5502         for (i = 0; i < k - 1; i++)
5503           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5504         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5505         foffset = offset;
5506         break;
5507       case 2:
5508         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5509         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5510         /* The SEM order is
5511 
5512          v_lb, {e_b}, v_rb,
5513          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5514          v_lt, reverse {e_t}, v_rt
5515          */
5516         {
5517           const PetscInt of   = 0;
5518           const PetscInt oeb  = of + PetscSqr(k - 1);
5519           const PetscInt oer  = oeb + (k - 1);
5520           const PetscInt oet  = oer + (k - 1);
5521           const PetscInt oel  = oet + (k - 1);
5522           const PetscInt ovlb = oel + (k - 1);
5523           const PetscInt ovrb = ovlb + 1;
5524           const PetscInt ovrt = ovrb + 1;
5525           const PetscInt ovlt = ovrt + 1;
5526           PetscInt       o;
5527 
5528           /* bottom */
5529           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5530           for (o = oeb; o < oer; ++o)
5531             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5532           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5533           /* middle */
5534           for (i = 0; i < k - 1; ++i) {
5535             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5536             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5537               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5538             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5539           }
5540           /* top */
5541           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5542           for (o = oel - 1; o >= oet; --o)
5543             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5544           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5545           foffset = offset;
5546         }
5547         break;
5548       case 3:
5549         /* The original hex closure is
5550 
5551          {c,
5552          f_b, f_t, f_f, f_b, f_r, f_l,
5553          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5554          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5555          */
5556         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5557         /* The SEM order is
5558          Bottom Slice
5559          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5560          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5561          v_blb, {e_bb}, v_brb,
5562 
5563          Middle Slice (j)
5564          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5565          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5566          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5567 
5568          Top Slice
5569          v_tlf, {e_tf}, v_trf,
5570          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5571          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5572          */
5573         {
5574           const PetscInt oc    = 0;
5575           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5576           const PetscInt oft   = ofb + PetscSqr(k - 1);
5577           const PetscInt off   = oft + PetscSqr(k - 1);
5578           const PetscInt ofk   = off + PetscSqr(k - 1);
5579           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5580           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5581           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5582           const PetscInt oebb  = oebl + (k - 1);
5583           const PetscInt oebr  = oebb + (k - 1);
5584           const PetscInt oebf  = oebr + (k - 1);
5585           const PetscInt oetf  = oebf + (k - 1);
5586           const PetscInt oetr  = oetf + (k - 1);
5587           const PetscInt oetb  = oetr + (k - 1);
5588           const PetscInt oetl  = oetb + (k - 1);
5589           const PetscInt oerf  = oetl + (k - 1);
5590           const PetscInt oelf  = oerf + (k - 1);
5591           const PetscInt oelb  = oelf + (k - 1);
5592           const PetscInt oerb  = oelb + (k - 1);
5593           const PetscInt ovblf = oerb + (k - 1);
5594           const PetscInt ovblb = ovblf + 1;
5595           const PetscInt ovbrb = ovblb + 1;
5596           const PetscInt ovbrf = ovbrb + 1;
5597           const PetscInt ovtlf = ovbrf + 1;
5598           const PetscInt ovtrf = ovtlf + 1;
5599           const PetscInt ovtrb = ovtrf + 1;
5600           const PetscInt ovtlb = ovtrb + 1;
5601           PetscInt       o, n;
5602 
5603           /* Bottom Slice */
5604           /*   bottom */
5605           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5606           for (o = oetf - 1; o >= oebf; --o)
5607             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5608           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5609           /*   middle */
5610           for (i = 0; i < k - 1; ++i) {
5611             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5612             for (n = 0; n < k - 1; ++n) {
5613               o = ofb + n * (k - 1) + i;
5614               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5615             }
5616             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5617           }
5618           /*   top */
5619           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5620           for (o = oebb; o < oebr; ++o)
5621             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5622           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5623 
5624           /* Middle Slice */
5625           for (j = 0; j < k - 1; ++j) {
5626             /*   bottom */
5627             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5628             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5629               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5630             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5631             /*   middle */
5632             for (i = 0; i < k - 1; ++i) {
5633               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5634               for (n = 0; n < k - 1; ++n)
5635                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5636               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5637             }
5638             /*   top */
5639             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5640             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5641               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5642             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5643           }
5644 
5645           /* Top Slice */
5646           /*   bottom */
5647           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5648           for (o = oetf; o < oetr; ++o)
5649             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5650           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5651           /*   middle */
5652           for (i = 0; i < k - 1; ++i) {
5653             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5654             for (n = 0; n < k - 1; ++n)
5655               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5656             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5657           }
5658           /*   top */
5659           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5660           for (o = oetl - 1; o >= oetb; --o)
5661             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5662           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5663 
5664           foffset = offset;
5665         }
5666         break;
5667       default:
5668         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5669       }
5670     }
5671     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5672     /* Check permutation */
5673     {
5674       PetscInt *check;
5675 
5676       PetscCall(PetscMalloc1(size, &check));
5677       for (i = 0; i < size; ++i) {
5678         check[i] = -1;
5679         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5680       }
5681       for (i = 0; i < size; ++i) check[perm[i]] = i;
5682       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5683       PetscCall(PetscFree(check));
5684     }
5685     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5686     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5687       PetscInt *loc_perm;
5688       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5689       for (PetscInt i = 0; i < size; i++) {
5690         loc_perm[i]        = perm[i];
5691         loc_perm[size + i] = size + perm[i];
5692       }
5693       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5694     }
5695   }
5696   PetscFunctionReturn(PETSC_SUCCESS);
5697 }
5698 
5699 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5700 {
5701   PetscDS  prob;
5702   PetscInt depth, Nf, h;
5703   DMLabel  label;
5704 
5705   PetscFunctionBeginHot;
5706   PetscCall(DMGetDS(dm, &prob));
5707   Nf      = prob->Nf;
5708   label   = dm->depthLabel;
5709   *dspace = NULL;
5710   if (field < Nf) {
5711     PetscObject disc = prob->disc[field];
5712 
5713     if (disc->classid == PETSCFE_CLASSID) {
5714       PetscDualSpace dsp;
5715 
5716       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5717       PetscCall(DMLabelGetNumValues(label, &depth));
5718       PetscCall(DMLabelGetValue(label, point, &h));
5719       h = depth - 1 - h;
5720       if (h) {
5721         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5722       } else {
5723         *dspace = dsp;
5724       }
5725     }
5726   }
5727   PetscFunctionReturn(PETSC_SUCCESS);
5728 }
5729 
5730 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5731 {
5732   PetscScalar       *array;
5733   const PetscScalar *vArray;
5734   const PetscInt    *cone, *coneO;
5735   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5736 
5737   PetscFunctionBeginHot;
5738   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5739   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5740   PetscCall(DMPlexGetCone(dm, point, &cone));
5741   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5742   if (!values || !*values) {
5743     if ((point >= pStart) && (point < pEnd)) {
5744       PetscInt dof;
5745 
5746       PetscCall(PetscSectionGetDof(section, point, &dof));
5747       size += dof;
5748     }
5749     for (p = 0; p < numPoints; ++p) {
5750       const PetscInt cp = cone[p];
5751       PetscInt       dof;
5752 
5753       if ((cp < pStart) || (cp >= pEnd)) continue;
5754       PetscCall(PetscSectionGetDof(section, cp, &dof));
5755       size += dof;
5756     }
5757     if (!values) {
5758       if (csize) *csize = size;
5759       PetscFunctionReturn(PETSC_SUCCESS);
5760     }
5761     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5762   } else {
5763     array = *values;
5764   }
5765   size = 0;
5766   PetscCall(VecGetArrayRead(v, &vArray));
5767   if ((point >= pStart) && (point < pEnd)) {
5768     PetscInt           dof, off, d;
5769     const PetscScalar *varr;
5770 
5771     PetscCall(PetscSectionGetDof(section, point, &dof));
5772     PetscCall(PetscSectionGetOffset(section, point, &off));
5773     varr = &vArray[off];
5774     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5775     size += dof;
5776   }
5777   for (p = 0; p < numPoints; ++p) {
5778     const PetscInt     cp = cone[p];
5779     PetscInt           o  = coneO[p];
5780     PetscInt           dof, off, d;
5781     const PetscScalar *varr;
5782 
5783     if ((cp < pStart) || (cp >= pEnd)) continue;
5784     PetscCall(PetscSectionGetDof(section, cp, &dof));
5785     PetscCall(PetscSectionGetOffset(section, cp, &off));
5786     varr = &vArray[off];
5787     if (o >= 0) {
5788       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5789     } else {
5790       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5791     }
5792     size += dof;
5793   }
5794   PetscCall(VecRestoreArrayRead(v, &vArray));
5795   if (!*values) {
5796     if (csize) *csize = size;
5797     *values = array;
5798   } else {
5799     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5800     *csize = size;
5801   }
5802   PetscFunctionReturn(PETSC_SUCCESS);
5803 }
5804 
5805 /* Compress out points not in the section */
5806 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5807 {
5808   const PetscInt np = *numPoints;
5809   PetscInt       pStart, pEnd, p, q;
5810 
5811   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5812   for (p = 0, q = 0; p < np; ++p) {
5813     const PetscInt r = points[p * 2];
5814     if ((r >= pStart) && (r < pEnd)) {
5815       points[q * 2]     = r;
5816       points[q * 2 + 1] = points[p * 2 + 1];
5817       ++q;
5818     }
5819   }
5820   *numPoints = q;
5821   return PETSC_SUCCESS;
5822 }
5823 
5824 /* Compressed closure does not apply closure permutation */
5825 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5826 {
5827   const PetscInt *cla = NULL;
5828   PetscInt        np, *pts = NULL;
5829 
5830   PetscFunctionBeginHot;
5831   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5832   if (!ornt && *clPoints) {
5833     PetscInt dof, off;
5834 
5835     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5836     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5837     PetscCall(ISGetIndices(*clPoints, &cla));
5838     np  = dof / 2;
5839     pts = (PetscInt *)&cla[off];
5840   } else {
5841     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
5842     PetscCall(CompressPoints_Private(section, &np, pts));
5843   }
5844   *numPoints = np;
5845   *points    = pts;
5846   *clp       = cla;
5847   PetscFunctionReturn(PETSC_SUCCESS);
5848 }
5849 
5850 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5851 {
5852   PetscFunctionBeginHot;
5853   if (!*clPoints) {
5854     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5855   } else {
5856     PetscCall(ISRestoreIndices(*clPoints, clp));
5857   }
5858   *numPoints = 0;
5859   *points    = NULL;
5860   *clSec     = NULL;
5861   *clPoints  = NULL;
5862   *clp       = NULL;
5863   PetscFunctionReturn(PETSC_SUCCESS);
5864 }
5865 
5866 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5867 {
5868   PetscInt            offset = 0, p;
5869   const PetscInt    **perms  = NULL;
5870   const PetscScalar **flips  = NULL;
5871 
5872   PetscFunctionBeginHot;
5873   *size = 0;
5874   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5875   for (p = 0; p < numPoints; p++) {
5876     const PetscInt     point = points[2 * p];
5877     const PetscInt    *perm  = perms ? perms[p] : NULL;
5878     const PetscScalar *flip  = flips ? flips[p] : NULL;
5879     PetscInt           dof, off, d;
5880     const PetscScalar *varr;
5881 
5882     PetscCall(PetscSectionGetDof(section, point, &dof));
5883     PetscCall(PetscSectionGetOffset(section, point, &off));
5884     varr = &vArray[off];
5885     if (clperm) {
5886       if (perm) {
5887         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5888       } else {
5889         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5890       }
5891       if (flip) {
5892         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5893       }
5894     } else {
5895       if (perm) {
5896         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5897       } else {
5898         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5899       }
5900       if (flip) {
5901         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5902       }
5903     }
5904     offset += dof;
5905   }
5906   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5907   *size = offset;
5908   PetscFunctionReturn(PETSC_SUCCESS);
5909 }
5910 
5911 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[])
5912 {
5913   PetscInt offset = 0, f;
5914 
5915   PetscFunctionBeginHot;
5916   *size = 0;
5917   for (f = 0; f < numFields; ++f) {
5918     PetscInt            p;
5919     const PetscInt    **perms = NULL;
5920     const PetscScalar **flips = NULL;
5921 
5922     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5923     for (p = 0; p < numPoints; p++) {
5924       const PetscInt     point = points[2 * p];
5925       PetscInt           fdof, foff, b;
5926       const PetscScalar *varr;
5927       const PetscInt    *perm = perms ? perms[p] : NULL;
5928       const PetscScalar *flip = flips ? flips[p] : NULL;
5929 
5930       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5931       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5932       varr = &vArray[foff];
5933       if (clperm) {
5934         if (perm) {
5935           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5936         } else {
5937           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5938         }
5939         if (flip) {
5940           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5941         }
5942       } else {
5943         if (perm) {
5944           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5945         } else {
5946           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5947         }
5948         if (flip) {
5949           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5950         }
5951       }
5952       offset += fdof;
5953     }
5954     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5955   }
5956   *size = offset;
5957   PetscFunctionReturn(PETSC_SUCCESS);
5958 }
5959 
5960 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
5961 {
5962   PetscSection    clSection;
5963   IS              clPoints;
5964   PetscInt       *points = NULL;
5965   const PetscInt *clp, *perm;
5966   PetscInt        depth, numFields, numPoints, asize;
5967 
5968   PetscFunctionBeginHot;
5969   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5970   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5971   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5972   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5973   PetscCall(DMPlexGetDepth(dm, &depth));
5974   PetscCall(PetscSectionGetNumFields(section, &numFields));
5975   if (depth == 1 && numFields < 2) {
5976     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5977     PetscFunctionReturn(PETSC_SUCCESS);
5978   }
5979   /* Get points */
5980   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
5981   /* Get sizes */
5982   asize = 0;
5983   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
5984     PetscInt dof;
5985     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5986     asize += dof;
5987   }
5988   if (values) {
5989     const PetscScalar *vArray;
5990     PetscInt           size;
5991 
5992     if (*values) {
5993       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);
5994     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5995     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
5996     PetscCall(VecGetArrayRead(v, &vArray));
5997     /* Get values */
5998     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5999     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6000     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6001     /* Cleanup array */
6002     PetscCall(VecRestoreArrayRead(v, &vArray));
6003   }
6004   if (csize) *csize = asize;
6005   /* Cleanup points */
6006   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6007   PetscFunctionReturn(PETSC_SUCCESS);
6008 }
6009 
6010 /*@C
6011   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6012 
6013   Not collective
6014 
6015   Input Parameters:
6016 + dm - The `DM`
6017 . section - The section describing the layout in `v`, or `NULL` to use the default section
6018 . v - The local vector
6019 - point - The point in the `DM`
6020 
6021   Input/Output Parameters:
6022 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6023 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6024            if the user provided `NULL`, it is a borrowed array and should not be freed
6025 
6026   Level: intermediate
6027 
6028   Notes:
6029   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6030   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6031   assembly function, and a user may already have allocated storage for this operation.
6032 
6033   A typical use could be
6034 .vb
6035    values = NULL;
6036    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6037    for (cl = 0; cl < clSize; ++cl) {
6038      <Compute on closure>
6039    }
6040    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6041 .ve
6042   or
6043 .vb
6044    PetscMalloc1(clMaxSize, &values);
6045    for (p = pStart; p < pEnd; ++p) {
6046      clSize = clMaxSize;
6047      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6048      for (cl = 0; cl < clSize; ++cl) {
6049        <Compute on closure>
6050      }
6051    }
6052    PetscFree(values);
6053 .ve
6054 
6055   Fortran Note:
6056   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6057 
6058 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6059 @*/
6060 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6061 {
6062   PetscFunctionBeginHot;
6063   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, v, point, 0, csize, values));
6064   PetscFunctionReturn(PETSC_SUCCESS);
6065 }
6066 
6067 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6068 {
6069   DMLabel            depthLabel;
6070   PetscSection       clSection;
6071   IS                 clPoints;
6072   PetscScalar       *array;
6073   const PetscScalar *vArray;
6074   PetscInt          *points = NULL;
6075   const PetscInt    *clp, *perm = NULL;
6076   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6077 
6078   PetscFunctionBeginHot;
6079   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6080   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6081   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6082   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6083   PetscCall(DMPlexGetDepth(dm, &mdepth));
6084   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6085   PetscCall(PetscSectionGetNumFields(section, &numFields));
6086   if (mdepth == 1 && numFields < 2) {
6087     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6088     PetscFunctionReturn(PETSC_SUCCESS);
6089   }
6090   /* Get points */
6091   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6092   for (clsize = 0, p = 0; p < Np; p++) {
6093     PetscInt dof;
6094     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6095     clsize += dof;
6096   }
6097   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6098   /* Filter points */
6099   for (p = 0; p < numPoints * 2; p += 2) {
6100     PetscInt dep;
6101 
6102     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6103     if (dep != depth) continue;
6104     points[Np * 2 + 0] = points[p];
6105     points[Np * 2 + 1] = points[p + 1];
6106     ++Np;
6107   }
6108   /* Get array */
6109   if (!values || !*values) {
6110     PetscInt asize = 0, dof;
6111 
6112     for (p = 0; p < Np * 2; p += 2) {
6113       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6114       asize += dof;
6115     }
6116     if (!values) {
6117       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6118       if (csize) *csize = asize;
6119       PetscFunctionReturn(PETSC_SUCCESS);
6120     }
6121     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6122   } else {
6123     array = *values;
6124   }
6125   PetscCall(VecGetArrayRead(v, &vArray));
6126   /* Get values */
6127   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6128   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6129   /* Cleanup points */
6130   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6131   /* Cleanup array */
6132   PetscCall(VecRestoreArrayRead(v, &vArray));
6133   if (!*values) {
6134     if (csize) *csize = size;
6135     *values = array;
6136   } else {
6137     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6138     *csize = size;
6139   }
6140   PetscFunctionReturn(PETSC_SUCCESS);
6141 }
6142 
6143 /*@C
6144   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6145 
6146   Not collective
6147 
6148   Input Parameters:
6149 + dm - The `DM`
6150 . section - The section describing the layout in `v`, or `NULL` to use the default section
6151 . v - The local vector
6152 . point - The point in the `DM`
6153 . csize - The number of values in the closure, or `NULL`
6154 - values - The array of values, which is a borrowed array and should not be freed
6155 
6156   Level: intermediate
6157 
6158   Note:
6159   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6160 
6161   Fortran Note:
6162   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6163 
6164 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6165 @*/
6166 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6167 {
6168   PetscInt size = 0;
6169 
6170   PetscFunctionBegin;
6171   /* Should work without recalculating size */
6172   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6173   *values = NULL;
6174   PetscFunctionReturn(PETSC_SUCCESS);
6175 }
6176 
6177 static inline void add(PetscScalar *x, PetscScalar y)
6178 {
6179   *x += y;
6180 }
6181 static inline void insert(PetscScalar *x, PetscScalar y)
6182 {
6183   *x = y;
6184 }
6185 
6186 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[])
6187 {
6188   PetscInt        cdof;  /* The number of constraints on this point */
6189   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6190   PetscScalar    *a;
6191   PetscInt        off, cind = 0, k;
6192 
6193   PetscFunctionBegin;
6194   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6195   PetscCall(PetscSectionGetOffset(section, point, &off));
6196   a = &array[off];
6197   if (!cdof || setBC) {
6198     if (clperm) {
6199       if (perm) {
6200         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6201       } else {
6202         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6203       }
6204     } else {
6205       if (perm) {
6206         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6207       } else {
6208         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6209       }
6210     }
6211   } else {
6212     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6213     if (clperm) {
6214       if (perm) {
6215         for (k = 0; k < dof; ++k) {
6216           if ((cind < cdof) && (k == cdofs[cind])) {
6217             ++cind;
6218             continue;
6219           }
6220           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6221         }
6222       } else {
6223         for (k = 0; k < dof; ++k) {
6224           if ((cind < cdof) && (k == cdofs[cind])) {
6225             ++cind;
6226             continue;
6227           }
6228           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6229         }
6230       }
6231     } else {
6232       if (perm) {
6233         for (k = 0; k < dof; ++k) {
6234           if ((cind < cdof) && (k == cdofs[cind])) {
6235             ++cind;
6236             continue;
6237           }
6238           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6239         }
6240       } else {
6241         for (k = 0; k < dof; ++k) {
6242           if ((cind < cdof) && (k == cdofs[cind])) {
6243             ++cind;
6244             continue;
6245           }
6246           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6247         }
6248       }
6249     }
6250   }
6251   PetscFunctionReturn(PETSC_SUCCESS);
6252 }
6253 
6254 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[])
6255 {
6256   PetscInt        cdof;  /* The number of constraints on this point */
6257   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6258   PetscScalar    *a;
6259   PetscInt        off, cind = 0, k;
6260 
6261   PetscFunctionBegin;
6262   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6263   PetscCall(PetscSectionGetOffset(section, point, &off));
6264   a = &array[off];
6265   if (cdof) {
6266     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6267     if (clperm) {
6268       if (perm) {
6269         for (k = 0; k < dof; ++k) {
6270           if ((cind < cdof) && (k == cdofs[cind])) {
6271             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6272             cind++;
6273           }
6274         }
6275       } else {
6276         for (k = 0; k < dof; ++k) {
6277           if ((cind < cdof) && (k == cdofs[cind])) {
6278             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6279             cind++;
6280           }
6281         }
6282       }
6283     } else {
6284       if (perm) {
6285         for (k = 0; k < dof; ++k) {
6286           if ((cind < cdof) && (k == cdofs[cind])) {
6287             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6288             cind++;
6289           }
6290         }
6291       } else {
6292         for (k = 0; k < dof; ++k) {
6293           if ((cind < cdof) && (k == cdofs[cind])) {
6294             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6295             cind++;
6296           }
6297         }
6298       }
6299     }
6300   }
6301   PetscFunctionReturn(PETSC_SUCCESS);
6302 }
6303 
6304 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[])
6305 {
6306   PetscScalar    *a;
6307   PetscInt        fdof, foff, fcdof, foffset = *offset;
6308   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6309   PetscInt        cind = 0, b;
6310 
6311   PetscFunctionBegin;
6312   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6313   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6314   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6315   a = &array[foff];
6316   if (!fcdof || setBC) {
6317     if (clperm) {
6318       if (perm) {
6319         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6320       } else {
6321         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6322       }
6323     } else {
6324       if (perm) {
6325         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6326       } else {
6327         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6328       }
6329     }
6330   } else {
6331     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6332     if (clperm) {
6333       if (perm) {
6334         for (b = 0; b < fdof; b++) {
6335           if ((cind < fcdof) && (b == fcdofs[cind])) {
6336             ++cind;
6337             continue;
6338           }
6339           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6340         }
6341       } else {
6342         for (b = 0; b < fdof; b++) {
6343           if ((cind < fcdof) && (b == fcdofs[cind])) {
6344             ++cind;
6345             continue;
6346           }
6347           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6348         }
6349       }
6350     } else {
6351       if (perm) {
6352         for (b = 0; b < fdof; b++) {
6353           if ((cind < fcdof) && (b == fcdofs[cind])) {
6354             ++cind;
6355             continue;
6356           }
6357           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6358         }
6359       } else {
6360         for (b = 0; b < fdof; b++) {
6361           if ((cind < fcdof) && (b == fcdofs[cind])) {
6362             ++cind;
6363             continue;
6364           }
6365           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6366         }
6367       }
6368     }
6369   }
6370   *offset += fdof;
6371   PetscFunctionReturn(PETSC_SUCCESS);
6372 }
6373 
6374 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[])
6375 {
6376   PetscScalar    *a;
6377   PetscInt        fdof, foff, fcdof, foffset = *offset;
6378   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6379   PetscInt        Nc, cind = 0, ncind = 0, b;
6380   PetscBool       ncSet, fcSet;
6381 
6382   PetscFunctionBegin;
6383   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6384   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6385   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6386   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6387   a = &array[foff];
6388   if (fcdof) {
6389     /* We just override fcdof and fcdofs with Ncc and comps */
6390     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6391     if (clperm) {
6392       if (perm) {
6393         if (comps) {
6394           for (b = 0; b < fdof; b++) {
6395             ncSet = fcSet = PETSC_FALSE;
6396             if (b % Nc == comps[ncind]) {
6397               ncind = (ncind + 1) % Ncc;
6398               ncSet = PETSC_TRUE;
6399             }
6400             if ((cind < fcdof) && (b == fcdofs[cind])) {
6401               ++cind;
6402               fcSet = PETSC_TRUE;
6403             }
6404             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6405           }
6406         } else {
6407           for (b = 0; b < fdof; b++) {
6408             if ((cind < fcdof) && (b == fcdofs[cind])) {
6409               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6410               ++cind;
6411             }
6412           }
6413         }
6414       } else {
6415         if (comps) {
6416           for (b = 0; b < fdof; b++) {
6417             ncSet = fcSet = PETSC_FALSE;
6418             if (b % Nc == comps[ncind]) {
6419               ncind = (ncind + 1) % Ncc;
6420               ncSet = PETSC_TRUE;
6421             }
6422             if ((cind < fcdof) && (b == fcdofs[cind])) {
6423               ++cind;
6424               fcSet = PETSC_TRUE;
6425             }
6426             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6427           }
6428         } else {
6429           for (b = 0; b < fdof; b++) {
6430             if ((cind < fcdof) && (b == fcdofs[cind])) {
6431               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6432               ++cind;
6433             }
6434           }
6435         }
6436       }
6437     } else {
6438       if (perm) {
6439         if (comps) {
6440           for (b = 0; b < fdof; b++) {
6441             ncSet = fcSet = PETSC_FALSE;
6442             if (b % Nc == comps[ncind]) {
6443               ncind = (ncind + 1) % Ncc;
6444               ncSet = PETSC_TRUE;
6445             }
6446             if ((cind < fcdof) && (b == fcdofs[cind])) {
6447               ++cind;
6448               fcSet = PETSC_TRUE;
6449             }
6450             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6451           }
6452         } else {
6453           for (b = 0; b < fdof; b++) {
6454             if ((cind < fcdof) && (b == fcdofs[cind])) {
6455               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6456               ++cind;
6457             }
6458           }
6459         }
6460       } else {
6461         if (comps) {
6462           for (b = 0; b < fdof; b++) {
6463             ncSet = fcSet = PETSC_FALSE;
6464             if (b % Nc == comps[ncind]) {
6465               ncind = (ncind + 1) % Ncc;
6466               ncSet = PETSC_TRUE;
6467             }
6468             if ((cind < fcdof) && (b == fcdofs[cind])) {
6469               ++cind;
6470               fcSet = PETSC_TRUE;
6471             }
6472             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6473           }
6474         } else {
6475           for (b = 0; b < fdof; b++) {
6476             if ((cind < fcdof) && (b == fcdofs[cind])) {
6477               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6478               ++cind;
6479             }
6480           }
6481         }
6482       }
6483     }
6484   }
6485   *offset += fdof;
6486   PetscFunctionReturn(PETSC_SUCCESS);
6487 }
6488 
6489 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6490 {
6491   PetscScalar    *array;
6492   const PetscInt *cone, *coneO;
6493   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6494 
6495   PetscFunctionBeginHot;
6496   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6497   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6498   PetscCall(DMPlexGetCone(dm, point, &cone));
6499   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6500   PetscCall(VecGetArray(v, &array));
6501   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6502     const PetscInt cp = !p ? point : cone[p - 1];
6503     const PetscInt o  = !p ? 0 : coneO[p - 1];
6504 
6505     if ((cp < pStart) || (cp >= pEnd)) {
6506       dof = 0;
6507       continue;
6508     }
6509     PetscCall(PetscSectionGetDof(section, cp, &dof));
6510     /* ADD_VALUES */
6511     {
6512       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6513       PetscScalar    *a;
6514       PetscInt        cdof, coff, cind = 0, k;
6515 
6516       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6517       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6518       a = &array[coff];
6519       if (!cdof) {
6520         if (o >= 0) {
6521           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6522         } else {
6523           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6524         }
6525       } else {
6526         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6527         if (o >= 0) {
6528           for (k = 0; k < dof; ++k) {
6529             if ((cind < cdof) && (k == cdofs[cind])) {
6530               ++cind;
6531               continue;
6532             }
6533             a[k] += values[off + k];
6534           }
6535         } else {
6536           for (k = 0; k < dof; ++k) {
6537             if ((cind < cdof) && (k == cdofs[cind])) {
6538               ++cind;
6539               continue;
6540             }
6541             a[k] += values[off + dof - k - 1];
6542           }
6543         }
6544       }
6545     }
6546   }
6547   PetscCall(VecRestoreArray(v, &array));
6548   PetscFunctionReturn(PETSC_SUCCESS);
6549 }
6550 
6551 /*@C
6552   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6553 
6554   Not collective
6555 
6556   Input Parameters:
6557 + dm - The `DM`
6558 . section - The section describing the layout in `v`, or `NULL` to use the default section
6559 . v - The local vector
6560 . point - The point in the `DM`
6561 . values - The array of values
6562 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6563          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6564 
6565   Level: intermediate
6566 
6567 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6568 @*/
6569 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6570 {
6571   PetscSection    clSection;
6572   IS              clPoints;
6573   PetscScalar    *array;
6574   PetscInt       *points = NULL;
6575   const PetscInt *clp, *clperm = NULL;
6576   PetscInt        depth, numFields, numPoints, p, clsize;
6577 
6578   PetscFunctionBeginHot;
6579   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6580   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6581   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6582   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6583   PetscCall(DMPlexGetDepth(dm, &depth));
6584   PetscCall(PetscSectionGetNumFields(section, &numFields));
6585   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6586     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6587     PetscFunctionReturn(PETSC_SUCCESS);
6588   }
6589   /* Get points */
6590   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6591   for (clsize = 0, p = 0; p < numPoints; p++) {
6592     PetscInt dof;
6593     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6594     clsize += dof;
6595   }
6596   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6597   /* Get array */
6598   PetscCall(VecGetArray(v, &array));
6599   /* Get values */
6600   if (numFields > 0) {
6601     PetscInt offset = 0, f;
6602     for (f = 0; f < numFields; ++f) {
6603       const PetscInt    **perms = NULL;
6604       const PetscScalar **flips = NULL;
6605 
6606       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6607       switch (mode) {
6608       case INSERT_VALUES:
6609         for (p = 0; p < numPoints; p++) {
6610           const PetscInt     point = points[2 * p];
6611           const PetscInt    *perm  = perms ? perms[p] : NULL;
6612           const PetscScalar *flip  = flips ? flips[p] : NULL;
6613           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6614         }
6615         break;
6616       case INSERT_ALL_VALUES:
6617         for (p = 0; p < numPoints; p++) {
6618           const PetscInt     point = points[2 * p];
6619           const PetscInt    *perm  = perms ? perms[p] : NULL;
6620           const PetscScalar *flip  = flips ? flips[p] : NULL;
6621           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6622         }
6623         break;
6624       case INSERT_BC_VALUES:
6625         for (p = 0; p < numPoints; p++) {
6626           const PetscInt     point = points[2 * p];
6627           const PetscInt    *perm  = perms ? perms[p] : NULL;
6628           const PetscScalar *flip  = flips ? flips[p] : NULL;
6629           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6630         }
6631         break;
6632       case ADD_VALUES:
6633         for (p = 0; p < numPoints; p++) {
6634           const PetscInt     point = points[2 * p];
6635           const PetscInt    *perm  = perms ? perms[p] : NULL;
6636           const PetscScalar *flip  = flips ? flips[p] : NULL;
6637           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6638         }
6639         break;
6640       case ADD_ALL_VALUES:
6641         for (p = 0; p < numPoints; p++) {
6642           const PetscInt     point = points[2 * p];
6643           const PetscInt    *perm  = perms ? perms[p] : NULL;
6644           const PetscScalar *flip  = flips ? flips[p] : NULL;
6645           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6646         }
6647         break;
6648       case ADD_BC_VALUES:
6649         for (p = 0; p < numPoints; p++) {
6650           const PetscInt     point = points[2 * p];
6651           const PetscInt    *perm  = perms ? perms[p] : NULL;
6652           const PetscScalar *flip  = flips ? flips[p] : NULL;
6653           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6654         }
6655         break;
6656       default:
6657         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6658       }
6659       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6660     }
6661   } else {
6662     PetscInt            dof, off;
6663     const PetscInt    **perms = NULL;
6664     const PetscScalar **flips = NULL;
6665 
6666     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6667     switch (mode) {
6668     case INSERT_VALUES:
6669       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6670         const PetscInt     point = points[2 * p];
6671         const PetscInt    *perm  = perms ? perms[p] : NULL;
6672         const PetscScalar *flip  = flips ? flips[p] : NULL;
6673         PetscCall(PetscSectionGetDof(section, point, &dof));
6674         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6675       }
6676       break;
6677     case INSERT_ALL_VALUES:
6678       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6679         const PetscInt     point = points[2 * p];
6680         const PetscInt    *perm  = perms ? perms[p] : NULL;
6681         const PetscScalar *flip  = flips ? flips[p] : NULL;
6682         PetscCall(PetscSectionGetDof(section, point, &dof));
6683         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6684       }
6685       break;
6686     case INSERT_BC_VALUES:
6687       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6688         const PetscInt     point = points[2 * p];
6689         const PetscInt    *perm  = perms ? perms[p] : NULL;
6690         const PetscScalar *flip  = flips ? flips[p] : NULL;
6691         PetscCall(PetscSectionGetDof(section, point, &dof));
6692         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6693       }
6694       break;
6695     case ADD_VALUES:
6696       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6697         const PetscInt     point = points[2 * p];
6698         const PetscInt    *perm  = perms ? perms[p] : NULL;
6699         const PetscScalar *flip  = flips ? flips[p] : NULL;
6700         PetscCall(PetscSectionGetDof(section, point, &dof));
6701         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6702       }
6703       break;
6704     case ADD_ALL_VALUES:
6705       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6706         const PetscInt     point = points[2 * p];
6707         const PetscInt    *perm  = perms ? perms[p] : NULL;
6708         const PetscScalar *flip  = flips ? flips[p] : NULL;
6709         PetscCall(PetscSectionGetDof(section, point, &dof));
6710         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6711       }
6712       break;
6713     case ADD_BC_VALUES:
6714       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6715         const PetscInt     point = points[2 * p];
6716         const PetscInt    *perm  = perms ? perms[p] : NULL;
6717         const PetscScalar *flip  = flips ? flips[p] : NULL;
6718         PetscCall(PetscSectionGetDof(section, point, &dof));
6719         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6720       }
6721       break;
6722     default:
6723       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6724     }
6725     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6726   }
6727   /* Cleanup points */
6728   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6729   /* Cleanup array */
6730   PetscCall(VecRestoreArray(v, &array));
6731   PetscFunctionReturn(PETSC_SUCCESS);
6732 }
6733 
6734 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6735 {
6736   const PetscInt *supp, *cone;
6737   PetscScalar    *a;
6738   PetscInt        dim, Ns, dof, off, n = 0;
6739 
6740   PetscFunctionBegin;
6741   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6742   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6743   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6744   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6745   if (PetscDefined(USE_DEBUG)) {
6746     PetscInt vStart, vEnd;
6747 
6748     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6749     PetscCheck(point >= vStart && point < vEnd, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " must be a vertex in [%" PetscInt_FMT ", %" PetscInt_FMT "]", point, vStart, vEnd);
6750   }
6751   PetscValidScalarPointer(values, 5);
6752 
6753   PetscCall(DMGetDimension(dm, &dim));
6754   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6755   PetscCall(DMPlexGetSupport(dm, point, &supp));
6756   PetscCheck(Ns == 2 * dim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " has support size %" PetscInt_FMT " != %" PetscInt_FMT, point, Ns, 2 * dim);
6757   PetscCall(VecGetArray(v, &a));
6758   PetscCall(PetscSectionGetDof(section, point, &dof));
6759   PetscCall(PetscSectionGetOffset(section, point, &off));
6760   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6761   for (PetscInt d = 0; d < dim; ++d) {
6762     // Left edge
6763     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6764     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6765     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6766     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6767     // Right edge
6768     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6769     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6770     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6771     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6772   }
6773   PetscCall(VecRestoreArray(v, &a));
6774   PetscFunctionReturn(PETSC_SUCCESS);
6775 }
6776 
6777 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6778 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6779 {
6780   PetscFunctionBegin;
6781   *contains = PETSC_TRUE;
6782   if (label) {
6783     PetscInt fdof;
6784 
6785     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6786     if (!*contains) {
6787       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6788       *offset += fdof;
6789       PetscFunctionReturn(PETSC_SUCCESS);
6790     }
6791   }
6792   PetscFunctionReturn(PETSC_SUCCESS);
6793 }
6794 
6795 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6796 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)
6797 {
6798   PetscSection    clSection;
6799   IS              clPoints;
6800   PetscScalar    *array;
6801   PetscInt       *points = NULL;
6802   const PetscInt *clp;
6803   PetscInt        numFields, numPoints, p;
6804   PetscInt        offset = 0, f;
6805 
6806   PetscFunctionBeginHot;
6807   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6808   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6809   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6810   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6811   PetscCall(PetscSectionGetNumFields(section, &numFields));
6812   /* Get points */
6813   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6814   /* Get array */
6815   PetscCall(VecGetArray(v, &array));
6816   /* Get values */
6817   for (f = 0; f < numFields; ++f) {
6818     const PetscInt    **perms = NULL;
6819     const PetscScalar **flips = NULL;
6820     PetscBool           contains;
6821 
6822     if (!fieldActive[f]) {
6823       for (p = 0; p < numPoints * 2; p += 2) {
6824         PetscInt fdof;
6825         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6826         offset += fdof;
6827       }
6828       continue;
6829     }
6830     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6831     switch (mode) {
6832     case INSERT_VALUES:
6833       for (p = 0; p < numPoints; p++) {
6834         const PetscInt     point = points[2 * p];
6835         const PetscInt    *perm  = perms ? perms[p] : NULL;
6836         const PetscScalar *flip  = flips ? flips[p] : NULL;
6837         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6838         if (!contains) continue;
6839         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6840       }
6841       break;
6842     case INSERT_ALL_VALUES:
6843       for (p = 0; p < numPoints; p++) {
6844         const PetscInt     point = points[2 * p];
6845         const PetscInt    *perm  = perms ? perms[p] : NULL;
6846         const PetscScalar *flip  = flips ? flips[p] : NULL;
6847         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6848         if (!contains) continue;
6849         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6850       }
6851       break;
6852     case INSERT_BC_VALUES:
6853       for (p = 0; p < numPoints; p++) {
6854         const PetscInt     point = points[2 * p];
6855         const PetscInt    *perm  = perms ? perms[p] : NULL;
6856         const PetscScalar *flip  = flips ? flips[p] : NULL;
6857         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6858         if (!contains) continue;
6859         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6860       }
6861       break;
6862     case ADD_VALUES:
6863       for (p = 0; p < numPoints; p++) {
6864         const PetscInt     point = points[2 * p];
6865         const PetscInt    *perm  = perms ? perms[p] : NULL;
6866         const PetscScalar *flip  = flips ? flips[p] : NULL;
6867         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6868         if (!contains) continue;
6869         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6870       }
6871       break;
6872     case ADD_ALL_VALUES:
6873       for (p = 0; p < numPoints; p++) {
6874         const PetscInt     point = points[2 * p];
6875         const PetscInt    *perm  = perms ? perms[p] : NULL;
6876         const PetscScalar *flip  = flips ? flips[p] : NULL;
6877         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6878         if (!contains) continue;
6879         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6880       }
6881       break;
6882     default:
6883       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6884     }
6885     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6886   }
6887   /* Cleanup points */
6888   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6889   /* Cleanup array */
6890   PetscCall(VecRestoreArray(v, &array));
6891   PetscFunctionReturn(PETSC_SUCCESS);
6892 }
6893 
6894 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6895 {
6896   PetscMPIInt rank;
6897   PetscInt    i, j;
6898 
6899   PetscFunctionBegin;
6900   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6901   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6902   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6903   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6904   numCIndices = numCIndices ? numCIndices : numRIndices;
6905   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6906   for (i = 0; i < numRIndices; i++) {
6907     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6908     for (j = 0; j < numCIndices; j++) {
6909 #if defined(PETSC_USE_COMPLEX)
6910       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6911 #else
6912       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6913 #endif
6914     }
6915     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6916   }
6917   PetscFunctionReturn(PETSC_SUCCESS);
6918 }
6919 
6920 /*
6921   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6922 
6923   Input Parameters:
6924 + section - The section for this data layout
6925 . islocal - Is the section (and thus indices being requested) local or global?
6926 . point   - The point contributing dofs with these indices
6927 . off     - The global offset of this point
6928 . loff    - The local offset of each field
6929 . setBC   - The flag determining whether to include indices of boundary values
6930 . perm    - A permutation of the dofs on this point, or NULL
6931 - indperm - A permutation of the entire indices array, or NULL
6932 
6933   Output Parameter:
6934 . indices - Indices for dofs on this point
6935 
6936   Level: developer
6937 
6938   Note: The indices could be local or global, depending on the value of 'off'.
6939 */
6940 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6941 {
6942   PetscInt        dof;   /* The number of unknowns on this point */
6943   PetscInt        cdof;  /* The number of constraints on this point */
6944   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6945   PetscInt        cind = 0, k;
6946 
6947   PetscFunctionBegin;
6948   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6949   PetscCall(PetscSectionGetDof(section, point, &dof));
6950   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6951   if (!cdof || setBC) {
6952     for (k = 0; k < dof; ++k) {
6953       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6954       const PetscInt ind    = indperm ? indperm[preind] : preind;
6955 
6956       indices[ind] = off + k;
6957     }
6958   } else {
6959     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6960     for (k = 0; k < dof; ++k) {
6961       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6962       const PetscInt ind    = indperm ? indperm[preind] : preind;
6963 
6964       if ((cind < cdof) && (k == cdofs[cind])) {
6965         /* Insert check for returning constrained indices */
6966         indices[ind] = -(off + k + 1);
6967         ++cind;
6968       } else {
6969         indices[ind] = off + k - (islocal ? 0 : cind);
6970       }
6971     }
6972   }
6973   *loff += dof;
6974   PetscFunctionReturn(PETSC_SUCCESS);
6975 }
6976 
6977 /*
6978  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6979 
6980  Input Parameters:
6981 + section - a section (global or local)
6982 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
6983 . point - point within section
6984 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6985 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6986 . setBC - identify constrained (boundary condition) points via involution.
6987 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6988 . permsoff - offset
6989 - indperm - index permutation
6990 
6991  Output Parameter:
6992 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6993 . indices - array to hold indices (as defined by section) of each dof associated with point
6994 
6995  Notes:
6996  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6997  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6998  in the local vector.
6999 
7000  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7001  significant).  It is invalid to call with a global section and setBC=true.
7002 
7003  Developer Note:
7004  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7005  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7006  offset could be obtained from the section instead of passing it explicitly as we do now.
7007 
7008  Example:
7009  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7010  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7011  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7012  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.
7013 
7014  Level: developer
7015 */
7016 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[])
7017 {
7018   PetscInt numFields, foff, f;
7019 
7020   PetscFunctionBegin;
7021   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7022   PetscCall(PetscSectionGetNumFields(section, &numFields));
7023   for (f = 0, foff = 0; f < numFields; ++f) {
7024     PetscInt        fdof, cfdof;
7025     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7026     PetscInt        cind = 0, b;
7027     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7028 
7029     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7030     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7031     if (!cfdof || setBC) {
7032       for (b = 0; b < fdof; ++b) {
7033         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7034         const PetscInt ind    = indperm ? indperm[preind] : preind;
7035 
7036         indices[ind] = off + foff + b;
7037       }
7038     } else {
7039       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7040       for (b = 0; b < fdof; ++b) {
7041         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7042         const PetscInt ind    = indperm ? indperm[preind] : preind;
7043 
7044         if ((cind < cfdof) && (b == fcdofs[cind])) {
7045           indices[ind] = -(off + foff + b + 1);
7046           ++cind;
7047         } else {
7048           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7049         }
7050       }
7051     }
7052     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7053     foffs[f] += fdof;
7054   }
7055   PetscFunctionReturn(PETSC_SUCCESS);
7056 }
7057 
7058 /*
7059   This version believes the globalSection offsets for each field, rather than just the point offset
7060 
7061  . foffs - The offset into 'indices' for each field, since it is segregated by field
7062 
7063  Notes:
7064  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7065  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7066 */
7067 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7068 {
7069   PetscInt numFields, foff, f;
7070 
7071   PetscFunctionBegin;
7072   PetscCall(PetscSectionGetNumFields(section, &numFields));
7073   for (f = 0; f < numFields; ++f) {
7074     PetscInt        fdof, cfdof;
7075     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7076     PetscInt        cind = 0, b;
7077     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7078 
7079     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7080     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7081     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7082     if (!cfdof) {
7083       for (b = 0; b < fdof; ++b) {
7084         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7085         const PetscInt ind    = indperm ? indperm[preind] : preind;
7086 
7087         indices[ind] = foff + b;
7088       }
7089     } else {
7090       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7091       for (b = 0; b < fdof; ++b) {
7092         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7093         const PetscInt ind    = indperm ? indperm[preind] : preind;
7094 
7095         if ((cind < cfdof) && (b == fcdofs[cind])) {
7096           indices[ind] = -(foff + b + 1);
7097           ++cind;
7098         } else {
7099           indices[ind] = foff + b - cind;
7100         }
7101       }
7102     }
7103     foffs[f] += fdof;
7104   }
7105   PetscFunctionReturn(PETSC_SUCCESS);
7106 }
7107 
7108 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)
7109 {
7110   Mat             cMat;
7111   PetscSection    aSec, cSec;
7112   IS              aIS;
7113   PetscInt        aStart = -1, aEnd = -1;
7114   const PetscInt *anchors;
7115   PetscInt        numFields, f, p, q, newP = 0;
7116   PetscInt        newNumPoints = 0, newNumIndices = 0;
7117   PetscInt       *newPoints, *indices, *newIndices;
7118   PetscInt        maxAnchor, maxDof;
7119   PetscInt        newOffsets[32];
7120   PetscInt       *pointMatOffsets[32];
7121   PetscInt       *newPointOffsets[32];
7122   PetscScalar    *pointMat[32];
7123   PetscScalar    *newValues      = NULL, *tmpValues;
7124   PetscBool       anyConstrained = PETSC_FALSE;
7125 
7126   PetscFunctionBegin;
7127   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7128   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7129   PetscCall(PetscSectionGetNumFields(section, &numFields));
7130 
7131   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7132   /* if there are point-to-point constraints */
7133   if (aSec) {
7134     PetscCall(PetscArrayzero(newOffsets, 32));
7135     PetscCall(ISGetIndices(aIS, &anchors));
7136     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7137     /* figure out how many points are going to be in the new element matrix
7138      * (we allow double counting, because it's all just going to be summed
7139      * into the global matrix anyway) */
7140     for (p = 0; p < 2 * numPoints; p += 2) {
7141       PetscInt b    = points[p];
7142       PetscInt bDof = 0, bSecDof;
7143 
7144       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7145       if (!bSecDof) continue;
7146       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7147       if (bDof) {
7148         /* this point is constrained */
7149         /* it is going to be replaced by its anchors */
7150         PetscInt bOff, q;
7151 
7152         anyConstrained = PETSC_TRUE;
7153         newNumPoints += bDof;
7154         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7155         for (q = 0; q < bDof; q++) {
7156           PetscInt a = anchors[bOff + q];
7157           PetscInt aDof;
7158 
7159           PetscCall(PetscSectionGetDof(section, a, &aDof));
7160           newNumIndices += aDof;
7161           for (f = 0; f < numFields; ++f) {
7162             PetscInt fDof;
7163 
7164             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7165             newOffsets[f + 1] += fDof;
7166           }
7167         }
7168       } else {
7169         /* this point is not constrained */
7170         newNumPoints++;
7171         newNumIndices += bSecDof;
7172         for (f = 0; f < numFields; ++f) {
7173           PetscInt fDof;
7174 
7175           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7176           newOffsets[f + 1] += fDof;
7177         }
7178       }
7179     }
7180   }
7181   if (!anyConstrained) {
7182     if (outNumPoints) *outNumPoints = 0;
7183     if (outNumIndices) *outNumIndices = 0;
7184     if (outPoints) *outPoints = NULL;
7185     if (outValues) *outValues = NULL;
7186     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7187     PetscFunctionReturn(PETSC_SUCCESS);
7188   }
7189 
7190   if (outNumPoints) *outNumPoints = newNumPoints;
7191   if (outNumIndices) *outNumIndices = newNumIndices;
7192 
7193   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7194 
7195   if (!outPoints && !outValues) {
7196     if (offsets) {
7197       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7198     }
7199     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7200     PetscFunctionReturn(PETSC_SUCCESS);
7201   }
7202 
7203   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7204 
7205   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7206 
7207   /* workspaces */
7208   if (numFields) {
7209     for (f = 0; f < numFields; f++) {
7210       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7211       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7212     }
7213   } else {
7214     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7215     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7216   }
7217 
7218   /* get workspaces for the point-to-point matrices */
7219   if (numFields) {
7220     PetscInt totalOffset, totalMatOffset;
7221 
7222     for (p = 0; p < numPoints; p++) {
7223       PetscInt b    = points[2 * p];
7224       PetscInt bDof = 0, bSecDof;
7225 
7226       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7227       if (!bSecDof) {
7228         for (f = 0; f < numFields; f++) {
7229           newPointOffsets[f][p + 1] = 0;
7230           pointMatOffsets[f][p + 1] = 0;
7231         }
7232         continue;
7233       }
7234       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7235       if (bDof) {
7236         for (f = 0; f < numFields; f++) {
7237           PetscInt fDof, q, bOff, allFDof = 0;
7238 
7239           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7240           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7241           for (q = 0; q < bDof; q++) {
7242             PetscInt a = anchors[bOff + q];
7243             PetscInt aFDof;
7244 
7245             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7246             allFDof += aFDof;
7247           }
7248           newPointOffsets[f][p + 1] = allFDof;
7249           pointMatOffsets[f][p + 1] = fDof * allFDof;
7250         }
7251       } else {
7252         for (f = 0; f < numFields; f++) {
7253           PetscInt fDof;
7254 
7255           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7256           newPointOffsets[f][p + 1] = fDof;
7257           pointMatOffsets[f][p + 1] = 0;
7258         }
7259       }
7260     }
7261     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7262       newPointOffsets[f][0] = totalOffset;
7263       pointMatOffsets[f][0] = totalMatOffset;
7264       for (p = 0; p < numPoints; p++) {
7265         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7266         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7267       }
7268       totalOffset    = newPointOffsets[f][numPoints];
7269       totalMatOffset = pointMatOffsets[f][numPoints];
7270       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7271     }
7272   } else {
7273     for (p = 0; p < numPoints; p++) {
7274       PetscInt b    = points[2 * p];
7275       PetscInt bDof = 0, bSecDof;
7276 
7277       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7278       if (!bSecDof) {
7279         newPointOffsets[0][p + 1] = 0;
7280         pointMatOffsets[0][p + 1] = 0;
7281         continue;
7282       }
7283       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7284       if (bDof) {
7285         PetscInt bOff, q, allDof = 0;
7286 
7287         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7288         for (q = 0; q < bDof; q++) {
7289           PetscInt a = anchors[bOff + q], aDof;
7290 
7291           PetscCall(PetscSectionGetDof(section, a, &aDof));
7292           allDof += aDof;
7293         }
7294         newPointOffsets[0][p + 1] = allDof;
7295         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7296       } else {
7297         newPointOffsets[0][p + 1] = bSecDof;
7298         pointMatOffsets[0][p + 1] = 0;
7299       }
7300     }
7301     newPointOffsets[0][0] = 0;
7302     pointMatOffsets[0][0] = 0;
7303     for (p = 0; p < numPoints; p++) {
7304       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7305       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7306     }
7307     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7308   }
7309 
7310   /* output arrays */
7311   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7312 
7313   /* get the point-to-point matrices; construct newPoints */
7314   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7315   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7316   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7317   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7318   if (numFields) {
7319     for (p = 0, newP = 0; p < numPoints; p++) {
7320       PetscInt b    = points[2 * p];
7321       PetscInt o    = points[2 * p + 1];
7322       PetscInt bDof = 0, bSecDof;
7323 
7324       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7325       if (!bSecDof) continue;
7326       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7327       if (bDof) {
7328         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7329 
7330         fStart[0] = 0;
7331         fEnd[0]   = 0;
7332         for (f = 0; f < numFields; f++) {
7333           PetscInt fDof;
7334 
7335           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7336           fStart[f + 1] = fStart[f] + fDof;
7337           fEnd[f + 1]   = fStart[f + 1];
7338         }
7339         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7340         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7341 
7342         fAnchorStart[0] = 0;
7343         fAnchorEnd[0]   = 0;
7344         for (f = 0; f < numFields; f++) {
7345           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7346 
7347           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7348           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7349         }
7350         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7351         for (q = 0; q < bDof; q++) {
7352           PetscInt a = anchors[bOff + q], aOff;
7353 
7354           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7355           newPoints[2 * (newP + q)]     = a;
7356           newPoints[2 * (newP + q) + 1] = 0;
7357           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7358           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7359         }
7360         newP += bDof;
7361 
7362         if (outValues) {
7363           /* get the point-to-point submatrix */
7364           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]));
7365         }
7366       } else {
7367         newPoints[2 * newP]     = b;
7368         newPoints[2 * newP + 1] = o;
7369         newP++;
7370       }
7371     }
7372   } else {
7373     for (p = 0; p < numPoints; p++) {
7374       PetscInt b    = points[2 * p];
7375       PetscInt o    = points[2 * p + 1];
7376       PetscInt bDof = 0, bSecDof;
7377 
7378       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7379       if (!bSecDof) continue;
7380       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7381       if (bDof) {
7382         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7383 
7384         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7385         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7386 
7387         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7388         for (q = 0; q < bDof; q++) {
7389           PetscInt a = anchors[bOff + q], aOff;
7390 
7391           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7392 
7393           newPoints[2 * (newP + q)]     = a;
7394           newPoints[2 * (newP + q) + 1] = 0;
7395           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7396           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7397         }
7398         newP += bDof;
7399 
7400         /* get the point-to-point submatrix */
7401         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7402       } else {
7403         newPoints[2 * newP]     = b;
7404         newPoints[2 * newP + 1] = o;
7405         newP++;
7406       }
7407     }
7408   }
7409 
7410   if (outValues) {
7411     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7412     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7413     /* multiply constraints on the right */
7414     if (numFields) {
7415       for (f = 0; f < numFields; f++) {
7416         PetscInt oldOff = offsets[f];
7417 
7418         for (p = 0; p < numPoints; p++) {
7419           PetscInt cStart = newPointOffsets[f][p];
7420           PetscInt b      = points[2 * p];
7421           PetscInt c, r, k;
7422           PetscInt dof;
7423 
7424           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7425           if (!dof) continue;
7426           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7427             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7428             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7429 
7430             for (r = 0; r < numIndices; r++) {
7431               for (c = 0; c < nCols; c++) {
7432                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7433               }
7434             }
7435           } else {
7436             /* copy this column as is */
7437             for (r = 0; r < numIndices; r++) {
7438               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7439             }
7440           }
7441           oldOff += dof;
7442         }
7443       }
7444     } else {
7445       PetscInt oldOff = 0;
7446       for (p = 0; p < numPoints; p++) {
7447         PetscInt cStart = newPointOffsets[0][p];
7448         PetscInt b      = points[2 * p];
7449         PetscInt c, r, k;
7450         PetscInt dof;
7451 
7452         PetscCall(PetscSectionGetDof(section, b, &dof));
7453         if (!dof) continue;
7454         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7455           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7456           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7457 
7458           for (r = 0; r < numIndices; r++) {
7459             for (c = 0; c < nCols; c++) {
7460               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7461             }
7462           }
7463         } else {
7464           /* copy this column as is */
7465           for (r = 0; r < numIndices; r++) {
7466             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7467           }
7468         }
7469         oldOff += dof;
7470       }
7471     }
7472 
7473     if (multiplyLeft) {
7474       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7475       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7476       /* multiply constraints transpose on the left */
7477       if (numFields) {
7478         for (f = 0; f < numFields; f++) {
7479           PetscInt oldOff = offsets[f];
7480 
7481           for (p = 0; p < numPoints; p++) {
7482             PetscInt rStart = newPointOffsets[f][p];
7483             PetscInt b      = points[2 * p];
7484             PetscInt c, r, k;
7485             PetscInt dof;
7486 
7487             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7488             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7489               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7490               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7491 
7492               for (r = 0; r < nRows; r++) {
7493                 for (c = 0; c < newNumIndices; c++) {
7494                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7495                 }
7496               }
7497             } else {
7498               /* copy this row as is */
7499               for (r = 0; r < dof; r++) {
7500                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7501               }
7502             }
7503             oldOff += dof;
7504           }
7505         }
7506       } else {
7507         PetscInt oldOff = 0;
7508 
7509         for (p = 0; p < numPoints; p++) {
7510           PetscInt rStart = newPointOffsets[0][p];
7511           PetscInt b      = points[2 * p];
7512           PetscInt c, r, k;
7513           PetscInt dof;
7514 
7515           PetscCall(PetscSectionGetDof(section, b, &dof));
7516           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7517             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7518             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7519 
7520             for (r = 0; r < nRows; r++) {
7521               for (c = 0; c < newNumIndices; c++) {
7522                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7523               }
7524             }
7525           } else {
7526             /* copy this row as is */
7527             for (r = 0; r < dof; r++) {
7528               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7529             }
7530           }
7531           oldOff += dof;
7532         }
7533       }
7534 
7535       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7536     } else {
7537       newValues = tmpValues;
7538     }
7539   }
7540 
7541   /* clean up */
7542   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7543   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7544 
7545   if (numFields) {
7546     for (f = 0; f < numFields; f++) {
7547       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7548       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7549       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7550     }
7551   } else {
7552     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7553     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7554     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7555   }
7556   PetscCall(ISRestoreIndices(aIS, &anchors));
7557 
7558   /* output */
7559   if (outPoints) {
7560     *outPoints = newPoints;
7561   } else {
7562     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7563   }
7564   if (outValues) *outValues = newValues;
7565   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7566   PetscFunctionReturn(PETSC_SUCCESS);
7567 }
7568 
7569 /*@C
7570   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7571 
7572   Not collective
7573 
7574   Input Parameters:
7575 + dm         - The `DM`
7576 . section    - The `PetscSection` describing the points (a local section)
7577 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7578 . point      - The point defining the closure
7579 - useClPerm  - Use the closure point permutation if available
7580 
7581   Output Parameters:
7582 + numIndices - The number of dof indices in the closure of point with the input sections
7583 . indices    - The dof indices
7584 . outOffsets - Array to write the field offsets into, or `NULL`
7585 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7586 
7587   Level: advanced
7588 
7589   Notes:
7590   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7591 
7592   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7593   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7594   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7595   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7596   indices (with the above semantics) are implied.
7597 
7598 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7599           `PetscSection`, `DMGetGlobalSection()`
7600 @*/
7601 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7602 {
7603   /* Closure ordering */
7604   PetscSection    clSection;
7605   IS              clPoints;
7606   const PetscInt *clp;
7607   PetscInt       *points;
7608   const PetscInt *clperm = NULL;
7609   /* Dof permutation and sign flips */
7610   const PetscInt    **perms[32] = {NULL};
7611   const PetscScalar **flips[32] = {NULL};
7612   PetscScalar        *valCopy   = NULL;
7613   /* Hanging node constraints */
7614   PetscInt    *pointsC = NULL;
7615   PetscScalar *valuesC = NULL;
7616   PetscInt     NclC, NiC;
7617 
7618   PetscInt *idx;
7619   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7620   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7621 
7622   PetscFunctionBeginHot;
7623   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7624   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7625   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7626   if (numIndices) PetscValidIntPointer(numIndices, 6);
7627   if (indices) PetscValidPointer(indices, 7);
7628   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7629   if (values) PetscValidPointer(values, 9);
7630   PetscCall(PetscSectionGetNumFields(section, &Nf));
7631   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7632   PetscCall(PetscArrayzero(offsets, 32));
7633   /* 1) Get points in closure */
7634   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7635   if (useClPerm) {
7636     PetscInt depth, clsize;
7637     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7638     for (clsize = 0, p = 0; p < Ncl; p++) {
7639       PetscInt dof;
7640       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7641       clsize += dof;
7642     }
7643     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7644   }
7645   /* 2) Get number of indices on these points and field offsets from section */
7646   for (p = 0; p < Ncl * 2; p += 2) {
7647     PetscInt dof, fdof;
7648 
7649     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7650     for (f = 0; f < Nf; ++f) {
7651       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7652       offsets[f + 1] += fdof;
7653     }
7654     Ni += dof;
7655   }
7656   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7657   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7658   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7659   for (f = 0; f < PetscMax(1, Nf); ++f) {
7660     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7661     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7662     /* may need to apply sign changes to the element matrix */
7663     if (values && flips[f]) {
7664       PetscInt foffset = offsets[f];
7665 
7666       for (p = 0; p < Ncl; ++p) {
7667         PetscInt           pnt  = points[2 * p], fdof;
7668         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7669 
7670         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7671         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7672         if (flip) {
7673           PetscInt i, j, k;
7674 
7675           if (!valCopy) {
7676             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7677             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7678             *values = valCopy;
7679           }
7680           for (i = 0; i < fdof; ++i) {
7681             PetscScalar fval = flip[i];
7682 
7683             for (k = 0; k < Ni; ++k) {
7684               valCopy[Ni * (foffset + i) + k] *= fval;
7685               valCopy[Ni * k + (foffset + i)] *= fval;
7686             }
7687           }
7688         }
7689         foffset += fdof;
7690       }
7691     }
7692   }
7693   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7694   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7695   if (NclC) {
7696     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7697     for (f = 0; f < PetscMax(1, Nf); ++f) {
7698       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7699       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7700     }
7701     for (f = 0; f < PetscMax(1, Nf); ++f) {
7702       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7703       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7704     }
7705     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7706     Ncl    = NclC;
7707     Ni     = NiC;
7708     points = pointsC;
7709     if (values) *values = valuesC;
7710   }
7711   /* 5) Calculate indices */
7712   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7713   if (Nf) {
7714     PetscInt  idxOff;
7715     PetscBool useFieldOffsets;
7716 
7717     if (outOffsets) {
7718       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7719     }
7720     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7721     if (useFieldOffsets) {
7722       for (p = 0; p < Ncl; ++p) {
7723         const PetscInt pnt = points[p * 2];
7724 
7725         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7726       }
7727     } else {
7728       for (p = 0; p < Ncl; ++p) {
7729         const PetscInt pnt = points[p * 2];
7730 
7731         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7732         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7733          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7734          * global section. */
7735         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7736       }
7737     }
7738   } else {
7739     PetscInt off = 0, idxOff;
7740 
7741     for (p = 0; p < Ncl; ++p) {
7742       const PetscInt  pnt  = points[p * 2];
7743       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7744 
7745       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7746       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7747        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7748       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7749     }
7750   }
7751   /* 6) Cleanup */
7752   for (f = 0; f < PetscMax(1, Nf); ++f) {
7753     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7754     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7755   }
7756   if (NclC) {
7757     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7758   } else {
7759     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7760   }
7761 
7762   if (numIndices) *numIndices = Ni;
7763   if (indices) *indices = idx;
7764   PetscFunctionReturn(PETSC_SUCCESS);
7765 }
7766 
7767 /*@C
7768   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7769 
7770   Not collective
7771 
7772   Input Parameters:
7773 + dm         - The `DM`
7774 . section    - The `PetscSection` describing the points (a local section)
7775 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7776 . point      - The point defining the closure
7777 - useClPerm  - Use the closure point permutation if available
7778 
7779   Output Parameters:
7780 + numIndices - The number of dof indices in the closure of point with the input sections
7781 . indices    - The dof indices
7782 . outOffsets - Array to write the field offsets into, or `NULL`
7783 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7784 
7785   Level: advanced
7786 
7787   Notes:
7788   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7789 
7790   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7791   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7792   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7793   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7794   indices (with the above semantics) are implied.
7795 
7796 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7797 @*/
7798 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7799 {
7800   PetscFunctionBegin;
7801   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7802   PetscValidPointer(indices, 7);
7803   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7804   PetscFunctionReturn(PETSC_SUCCESS);
7805 }
7806 
7807 /*@C
7808   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7809 
7810   Not collective
7811 
7812   Input Parameters:
7813 + dm - The `DM`
7814 . section - The section describing the layout in `v`, or `NULL` to use the default section
7815 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7816 . A - The matrix
7817 . point - The point in the `DM`
7818 . values - The array of values
7819 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7820 
7821   Level: intermediate
7822 
7823 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7824 @*/
7825 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7826 {
7827   DM_Plex           *mesh = (DM_Plex *)dm->data;
7828   PetscInt          *indices;
7829   PetscInt           numIndices;
7830   const PetscScalar *valuesOrig = values;
7831   PetscErrorCode     ierr;
7832 
7833   PetscFunctionBegin;
7834   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7835   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7836   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7837   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7838   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7839   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7840 
7841   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7842 
7843   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7844   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7845   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7846   if (ierr) {
7847     PetscMPIInt rank;
7848 
7849     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7850     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7851     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7852     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7853     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7854     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7855   }
7856   if (mesh->printFEM > 1) {
7857     PetscInt i;
7858     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7859     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7860     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7861   }
7862 
7863   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7864   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7865   PetscFunctionReturn(PETSC_SUCCESS);
7866 }
7867 
7868 /*@C
7869   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7870 
7871   Not collective
7872 
7873   Input Parameters:
7874 + dmRow - The `DM` for the row fields
7875 . sectionRow - The section describing the layout, or `NULL` to use the default section in `dmRow`
7876 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7877 . dmCol - The `DM` for the column fields
7878 . sectionCol - The section describing the layout, or `NULL` to use the default section in `dmCol`
7879 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7880 . A - The matrix
7881 . point - The point in the `DM`
7882 . values - The array of values
7883 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7884 
7885   Level: intermediate
7886 
7887 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7888 @*/
7889 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7890 {
7891   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7892   PetscInt          *indicesRow, *indicesCol;
7893   PetscInt           numIndicesRow, numIndicesCol;
7894   const PetscScalar *valuesOrig = values;
7895   PetscErrorCode     ierr;
7896 
7897   PetscFunctionBegin;
7898   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7899   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7900   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7901   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7902   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7903   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7904   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7905   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7906   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7907   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7908   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7909 
7910   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7911   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7912 
7913   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7914   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7915   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7916   if (ierr) {
7917     PetscMPIInt rank;
7918 
7919     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7920     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7921     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7922     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7923     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7924     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7925   }
7926 
7927   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7928   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7929   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7930   PetscFunctionReturn(PETSC_SUCCESS);
7931 }
7932 
7933 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7934 {
7935   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7936   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7937   PetscInt       *cpoints = NULL;
7938   PetscInt       *findices, *cindices;
7939   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7940   PetscInt        foffsets[32], coffsets[32];
7941   DMPolytopeType  ct;
7942   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7943   PetscErrorCode  ierr;
7944 
7945   PetscFunctionBegin;
7946   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7947   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7948   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7949   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7950   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7951   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7952   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7953   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7954   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7955   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7956   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7957   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7958   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7959   PetscCall(PetscArrayzero(foffsets, 32));
7960   PetscCall(PetscArrayzero(coffsets, 32));
7961   /* Column indices */
7962   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7963   maxFPoints = numCPoints;
7964   /* Compress out points not in the section */
7965   /*   TODO: Squeeze out points with 0 dof as well */
7966   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7967   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7968     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7969       cpoints[q * 2]     = cpoints[p];
7970       cpoints[q * 2 + 1] = cpoints[p + 1];
7971       ++q;
7972     }
7973   }
7974   numCPoints = q;
7975   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7976     PetscInt fdof;
7977 
7978     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7979     if (!dof) continue;
7980     for (f = 0; f < numFields; ++f) {
7981       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7982       coffsets[f + 1] += fdof;
7983     }
7984     numCIndices += dof;
7985   }
7986   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7987   /* Row indices */
7988   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7989   {
7990     DMPlexTransform tr;
7991     DMPolytopeType *rct;
7992     PetscInt       *rsize, *rcone, *rornt, Nt;
7993 
7994     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7995     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7996     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7997     numSubcells = rsize[Nt - 1];
7998     PetscCall(DMPlexTransformDestroy(&tr));
7999   }
8000   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8001   for (r = 0, q = 0; r < numSubcells; ++r) {
8002     /* TODO Map from coarse to fine cells */
8003     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8004     /* Compress out points not in the section */
8005     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8006     for (p = 0; p < numFPoints * 2; p += 2) {
8007       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8008         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8009         if (!dof) continue;
8010         for (s = 0; s < q; ++s)
8011           if (fpoints[p] == ftotpoints[s * 2]) break;
8012         if (s < q) continue;
8013         ftotpoints[q * 2]     = fpoints[p];
8014         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8015         ++q;
8016       }
8017     }
8018     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8019   }
8020   numFPoints = q;
8021   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8022     PetscInt fdof;
8023 
8024     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8025     if (!dof) continue;
8026     for (f = 0; f < numFields; ++f) {
8027       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8028       foffsets[f + 1] += fdof;
8029     }
8030     numFIndices += dof;
8031   }
8032   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8033 
8034   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8035   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8036   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8037   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8038   if (numFields) {
8039     const PetscInt **permsF[32] = {NULL};
8040     const PetscInt **permsC[32] = {NULL};
8041 
8042     for (f = 0; f < numFields; f++) {
8043       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8044       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8045     }
8046     for (p = 0; p < numFPoints; p++) {
8047       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8048       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8049     }
8050     for (p = 0; p < numCPoints; p++) {
8051       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8052       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8053     }
8054     for (f = 0; f < numFields; f++) {
8055       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8056       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8057     }
8058   } else {
8059     const PetscInt **permsF = NULL;
8060     const PetscInt **permsC = NULL;
8061 
8062     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8063     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8064     for (p = 0, off = 0; p < numFPoints; p++) {
8065       const PetscInt *perm = permsF ? permsF[p] : NULL;
8066 
8067       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8068       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8069     }
8070     for (p = 0, off = 0; p < numCPoints; p++) {
8071       const PetscInt *perm = permsC ? permsC[p] : NULL;
8072 
8073       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8074       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8075     }
8076     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8077     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8078   }
8079   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8080   /* TODO: flips */
8081   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8082   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8083   if (ierr) {
8084     PetscMPIInt rank;
8085 
8086     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8087     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8088     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8089     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8090     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8091   }
8092   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8093   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8094   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8095   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8096   PetscFunctionReturn(PETSC_SUCCESS);
8097 }
8098 
8099 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8100 {
8101   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8102   PetscInt       *cpoints = NULL;
8103   PetscInt        foffsets[32], coffsets[32];
8104   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8105   DMPolytopeType  ct;
8106   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8107 
8108   PetscFunctionBegin;
8109   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8110   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8111   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8112   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8113   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8114   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8115   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8116   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8117   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8118   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8119   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8120   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8121   PetscCall(PetscArrayzero(foffsets, 32));
8122   PetscCall(PetscArrayzero(coffsets, 32));
8123   /* Column indices */
8124   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8125   maxFPoints = numCPoints;
8126   /* Compress out points not in the section */
8127   /*   TODO: Squeeze out points with 0 dof as well */
8128   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8129   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8130     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8131       cpoints[q * 2]     = cpoints[p];
8132       cpoints[q * 2 + 1] = cpoints[p + 1];
8133       ++q;
8134     }
8135   }
8136   numCPoints = q;
8137   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8138     PetscInt fdof;
8139 
8140     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8141     if (!dof) continue;
8142     for (f = 0; f < numFields; ++f) {
8143       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8144       coffsets[f + 1] += fdof;
8145     }
8146     numCIndices += dof;
8147   }
8148   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8149   /* Row indices */
8150   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8151   {
8152     DMPlexTransform tr;
8153     DMPolytopeType *rct;
8154     PetscInt       *rsize, *rcone, *rornt, Nt;
8155 
8156     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8157     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8158     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8159     numSubcells = rsize[Nt - 1];
8160     PetscCall(DMPlexTransformDestroy(&tr));
8161   }
8162   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8163   for (r = 0, q = 0; r < numSubcells; ++r) {
8164     /* TODO Map from coarse to fine cells */
8165     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8166     /* Compress out points not in the section */
8167     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8168     for (p = 0; p < numFPoints * 2; p += 2) {
8169       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8170         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8171         if (!dof) continue;
8172         for (s = 0; s < q; ++s)
8173           if (fpoints[p] == ftotpoints[s * 2]) break;
8174         if (s < q) continue;
8175         ftotpoints[q * 2]     = fpoints[p];
8176         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8177         ++q;
8178       }
8179     }
8180     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8181   }
8182   numFPoints = q;
8183   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8184     PetscInt fdof;
8185 
8186     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8187     if (!dof) continue;
8188     for (f = 0; f < numFields; ++f) {
8189       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8190       foffsets[f + 1] += fdof;
8191     }
8192     numFIndices += dof;
8193   }
8194   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8195 
8196   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8197   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8198   if (numFields) {
8199     const PetscInt **permsF[32] = {NULL};
8200     const PetscInt **permsC[32] = {NULL};
8201 
8202     for (f = 0; f < numFields; f++) {
8203       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8204       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8205     }
8206     for (p = 0; p < numFPoints; p++) {
8207       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8208       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8209     }
8210     for (p = 0; p < numCPoints; p++) {
8211       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8212       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8213     }
8214     for (f = 0; f < numFields; f++) {
8215       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8216       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8217     }
8218   } else {
8219     const PetscInt **permsF = NULL;
8220     const PetscInt **permsC = NULL;
8221 
8222     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8223     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8224     for (p = 0, off = 0; p < numFPoints; p++) {
8225       const PetscInt *perm = permsF ? permsF[p] : NULL;
8226 
8227       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8228       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8229     }
8230     for (p = 0, off = 0; p < numCPoints; p++) {
8231       const PetscInt *perm = permsC ? permsC[p] : NULL;
8232 
8233       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8234       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8235     }
8236     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8237     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8238   }
8239   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8240   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8241   PetscFunctionReturn(PETSC_SUCCESS);
8242 }
8243 
8244 /*@C
8245   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8246 
8247   Input Parameter:
8248 . dm   - The `DMPLEX` object
8249 
8250   Output Parameter:
8251 . cellHeight - The height of a cell
8252 
8253   Level: developer
8254 
8255 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8256 @*/
8257 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8258 {
8259   DM_Plex *mesh = (DM_Plex *)dm->data;
8260 
8261   PetscFunctionBegin;
8262   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8263   PetscValidIntPointer(cellHeight, 2);
8264   *cellHeight = mesh->vtkCellHeight;
8265   PetscFunctionReturn(PETSC_SUCCESS);
8266 }
8267 
8268 /*@C
8269   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8270 
8271   Input Parameters:
8272 + dm   - The `DMPLEX` object
8273 - cellHeight - The height of a cell
8274 
8275   Level: developer
8276 
8277 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8278 @*/
8279 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8280 {
8281   DM_Plex *mesh = (DM_Plex *)dm->data;
8282 
8283   PetscFunctionBegin;
8284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8285   mesh->vtkCellHeight = cellHeight;
8286   PetscFunctionReturn(PETSC_SUCCESS);
8287 }
8288 
8289 /*@
8290   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8291 
8292   Input Parameter:
8293 . dm - The `DMPLEX` object
8294 
8295   Output Parameters:
8296 + gcStart - The first ghost cell, or `NULL`
8297 - gcEnd   - The upper bound on ghost cells, or `NULL`
8298 
8299   Level: advanced
8300 
8301 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8302 @*/
8303 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8304 {
8305   DMLabel ctLabel;
8306 
8307   PetscFunctionBegin;
8308   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8309   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8310   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8311   // Reset label for fast lookup
8312   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8313   PetscFunctionReturn(PETSC_SUCCESS);
8314 }
8315 
8316 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8317 {
8318   PetscSection section, globalSection;
8319   PetscInt    *numbers, p;
8320 
8321   PetscFunctionBegin;
8322   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8323   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8324   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8325   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8326   PetscCall(PetscSectionSetUp(section));
8327   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8328   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8329   for (p = pStart; p < pEnd; ++p) {
8330     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8331     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8332     else numbers[p - pStart] += shift;
8333   }
8334   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8335   if (globalSize) {
8336     PetscLayout layout;
8337     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8338     PetscCall(PetscLayoutGetSize(layout, globalSize));
8339     PetscCall(PetscLayoutDestroy(&layout));
8340   }
8341   PetscCall(PetscSectionDestroy(&section));
8342   PetscCall(PetscSectionDestroy(&globalSection));
8343   PetscFunctionReturn(PETSC_SUCCESS);
8344 }
8345 
8346 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8347 {
8348   PetscInt cellHeight, cStart, cEnd;
8349 
8350   PetscFunctionBegin;
8351   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8352   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8353   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8354   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8355   PetscFunctionReturn(PETSC_SUCCESS);
8356 }
8357 
8358 /*@
8359   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8360 
8361   Input Parameter:
8362 . dm   - The `DMPLEX` object
8363 
8364   Output Parameter:
8365 . globalCellNumbers - Global cell numbers for all cells on this process
8366 
8367   Level: developer
8368 
8369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8370 @*/
8371 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8372 {
8373   DM_Plex *mesh = (DM_Plex *)dm->data;
8374 
8375   PetscFunctionBegin;
8376   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8377   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8378   *globalCellNumbers = mesh->globalCellNumbers;
8379   PetscFunctionReturn(PETSC_SUCCESS);
8380 }
8381 
8382 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8383 {
8384   PetscInt vStart, vEnd;
8385 
8386   PetscFunctionBegin;
8387   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8388   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8389   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8390   PetscFunctionReturn(PETSC_SUCCESS);
8391 }
8392 
8393 /*@
8394   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8395 
8396   Input Parameter:
8397 . dm   - The `DMPLEX` object
8398 
8399   Output Parameter:
8400 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8401 
8402   Level: developer
8403 
8404 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8405 @*/
8406 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8407 {
8408   DM_Plex *mesh = (DM_Plex *)dm->data;
8409 
8410   PetscFunctionBegin;
8411   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8412   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8413   *globalVertexNumbers = mesh->globalVertexNumbers;
8414   PetscFunctionReturn(PETSC_SUCCESS);
8415 }
8416 
8417 /*@
8418   DMPlexCreatePointNumbering - Create a global numbering for all points.
8419 
8420   Collective
8421 
8422   Input Parameter:
8423 . dm   - The `DMPLEX` object
8424 
8425   Output Parameter:
8426 . globalPointNumbers - Global numbers for all points on this process
8427 
8428   Level: developer
8429 
8430   Notes:
8431   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8432   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8433   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8434   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8435 
8436   The partitioned mesh is
8437 ```
8438  (2)--0--(3)--1--(4)    (1)--0--(2)
8439 ```
8440   and its global numbering is
8441 ```
8442   (3)--0--(4)--1--(5)--2--(6)
8443 ```
8444   Then the global numbering is provided as
8445 ```
8446 [0] Number of indices in set 5
8447 [0] 0 0
8448 [0] 1 1
8449 [0] 2 3
8450 [0] 3 4
8451 [0] 4 -6
8452 [1] Number of indices in set 3
8453 [1] 0 2
8454 [1] 1 5
8455 [1] 2 6
8456 ```
8457 
8458 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8459 @*/
8460 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8461 {
8462   IS        nums[4];
8463   PetscInt  depths[4], gdepths[4], starts[4];
8464   PetscInt  depth, d, shift = 0;
8465   PetscBool empty = PETSC_FALSE;
8466 
8467   PetscFunctionBegin;
8468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8469   PetscCall(DMPlexGetDepth(dm, &depth));
8470   // For unstratified meshes use dim instead of depth
8471   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8472   // If any stratum is empty, we must mark all empty
8473   for (d = 0; d <= depth; ++d) {
8474     PetscInt end;
8475 
8476     depths[d] = depth - d;
8477     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8478     if (!(starts[d] - end)) empty = PETSC_TRUE;
8479   }
8480   if (empty)
8481     for (d = 0; d <= depth; ++d) {
8482       depths[d] = -1;
8483       starts[d] = -1;
8484     }
8485   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8486   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8487   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]);
8488   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8489   for (d = 0; d <= depth; ++d) {
8490     PetscInt pStart, pEnd, gsize;
8491 
8492     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8493     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8494     shift += gsize;
8495   }
8496   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8497   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8498   PetscFunctionReturn(PETSC_SUCCESS);
8499 }
8500 
8501 /*@
8502   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8503 
8504   Input Parameter:
8505 . dm - The `DMPLEX` object
8506 
8507   Output Parameter:
8508 . ranks - The rank field
8509 
8510   Options Database Key:
8511 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8512 
8513   Level: intermediate
8514 
8515 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8516 @*/
8517 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8518 {
8519   DM             rdm;
8520   PetscFE        fe;
8521   PetscScalar   *r;
8522   PetscMPIInt    rank;
8523   DMPolytopeType ct;
8524   PetscInt       dim, cStart, cEnd, c;
8525   PetscBool      simplex;
8526 
8527   PetscFunctionBeginUser;
8528   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8529   PetscValidPointer(ranks, 2);
8530   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8531   PetscCall(DMClone(dm, &rdm));
8532   PetscCall(DMGetDimension(rdm, &dim));
8533   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8534   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8535   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8536   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8537   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8538   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8539   PetscCall(PetscFEDestroy(&fe));
8540   PetscCall(DMCreateDS(rdm));
8541   PetscCall(DMCreateGlobalVector(rdm, ranks));
8542   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8543   PetscCall(VecGetArray(*ranks, &r));
8544   for (c = cStart; c < cEnd; ++c) {
8545     PetscScalar *lr;
8546 
8547     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8548     if (lr) *lr = rank;
8549   }
8550   PetscCall(VecRestoreArray(*ranks, &r));
8551   PetscCall(DMDestroy(&rdm));
8552   PetscFunctionReturn(PETSC_SUCCESS);
8553 }
8554 
8555 /*@
8556   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8557 
8558   Input Parameters:
8559 + dm    - The `DMPLEX`
8560 - label - The `DMLabel`
8561 
8562   Output Parameter:
8563 . val - The label value field
8564 
8565   Options Database Key:
8566 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8567 
8568   Level: intermediate
8569 
8570 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8571 @*/
8572 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8573 {
8574   DM           rdm;
8575   PetscFE      fe;
8576   PetscScalar *v;
8577   PetscInt     dim, cStart, cEnd, c;
8578 
8579   PetscFunctionBeginUser;
8580   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8581   PetscValidPointer(label, 2);
8582   PetscValidPointer(val, 3);
8583   PetscCall(DMClone(dm, &rdm));
8584   PetscCall(DMGetDimension(rdm, &dim));
8585   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8586   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8587   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8588   PetscCall(PetscFEDestroy(&fe));
8589   PetscCall(DMCreateDS(rdm));
8590   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8591   PetscCall(DMCreateGlobalVector(rdm, val));
8592   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8593   PetscCall(VecGetArray(*val, &v));
8594   for (c = cStart; c < cEnd; ++c) {
8595     PetscScalar *lv;
8596     PetscInt     cval;
8597 
8598     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8599     PetscCall(DMLabelGetValue(label, c, &cval));
8600     *lv = cval;
8601   }
8602   PetscCall(VecRestoreArray(*val, &v));
8603   PetscCall(DMDestroy(&rdm));
8604   PetscFunctionReturn(PETSC_SUCCESS);
8605 }
8606 
8607 /*@
8608   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8609 
8610   Input Parameter:
8611 . dm - The `DMPLEX` object
8612 
8613   Level: developer
8614 
8615   Notes:
8616   This is a useful diagnostic when creating meshes programmatically.
8617 
8618   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8619 
8620 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8621 @*/
8622 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8623 {
8624   PetscSection    coneSection, supportSection;
8625   const PetscInt *cone, *support;
8626   PetscInt        coneSize, c, supportSize, s;
8627   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8628   PetscBool       storagecheck = PETSC_TRUE;
8629 
8630   PetscFunctionBegin;
8631   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8632   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8633   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8634   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8635   /* Check that point p is found in the support of its cone points, and vice versa */
8636   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8637   for (p = pStart; p < pEnd; ++p) {
8638     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8639     PetscCall(DMPlexGetCone(dm, p, &cone));
8640     for (c = 0; c < coneSize; ++c) {
8641       PetscBool dup = PETSC_FALSE;
8642       PetscInt  d;
8643       for (d = c - 1; d >= 0; --d) {
8644         if (cone[c] == cone[d]) {
8645           dup = PETSC_TRUE;
8646           break;
8647         }
8648       }
8649       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8650       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8651       for (s = 0; s < supportSize; ++s) {
8652         if (support[s] == p) break;
8653       }
8654       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8655         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8656         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8657         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8658         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8659         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8660         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8661         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]);
8662         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8663       }
8664     }
8665     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8666     if (p != pp) {
8667       storagecheck = PETSC_FALSE;
8668       continue;
8669     }
8670     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8671     PetscCall(DMPlexGetSupport(dm, p, &support));
8672     for (s = 0; s < supportSize; ++s) {
8673       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8674       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8675       for (c = 0; c < coneSize; ++c) {
8676         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8677         if (cone[c] != pp) {
8678           c = 0;
8679           break;
8680         }
8681         if (cone[c] == p) break;
8682       }
8683       if (c >= coneSize) {
8684         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8685         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8686         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8687         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8688         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8689         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8690         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8691       }
8692     }
8693   }
8694   if (storagecheck) {
8695     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8696     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8697     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8698   }
8699   PetscFunctionReturn(PETSC_SUCCESS);
8700 }
8701 
8702 /*
8703   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.
8704 */
8705 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8706 {
8707   DMPolytopeType  cct;
8708   PetscInt        ptpoints[4];
8709   const PetscInt *cone, *ccone, *ptcone;
8710   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8711 
8712   PetscFunctionBegin;
8713   *unsplit = 0;
8714   switch (ct) {
8715   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8716     ptpoints[npt++] = c;
8717     break;
8718   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8719     PetscCall(DMPlexGetCone(dm, c, &cone));
8720     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8721     for (cp = 0; cp < coneSize; ++cp) {
8722       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8723       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8724     }
8725     break;
8726   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8727   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8728     PetscCall(DMPlexGetCone(dm, c, &cone));
8729     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8730     for (cp = 0; cp < coneSize; ++cp) {
8731       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8732       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8733       for (ccp = 0; ccp < cconeSize; ++ccp) {
8734         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8735         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8736           PetscInt p;
8737           for (p = 0; p < npt; ++p)
8738             if (ptpoints[p] == ccone[ccp]) break;
8739           if (p == npt) ptpoints[npt++] = ccone[ccp];
8740         }
8741       }
8742     }
8743     break;
8744   default:
8745     break;
8746   }
8747   for (pt = 0; pt < npt; ++pt) {
8748     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8749     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8750   }
8751   PetscFunctionReturn(PETSC_SUCCESS);
8752 }
8753 
8754 /*@
8755   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8756 
8757   Input Parameters:
8758 + dm - The `DMPLEX` object
8759 - cellHeight - Normally 0
8760 
8761   Level: developer
8762 
8763   Notes:
8764   This is a useful diagnostic when creating meshes programmatically.
8765   Currently applicable only to homogeneous simplex or tensor meshes.
8766 
8767   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8768 
8769 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8770 @*/
8771 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8772 {
8773   DMPlexInterpolatedFlag interp;
8774   DMPolytopeType         ct;
8775   PetscInt               vStart, vEnd, cStart, cEnd, c;
8776 
8777   PetscFunctionBegin;
8778   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8779   PetscCall(DMPlexIsInterpolated(dm, &interp));
8780   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8781   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8782   for (c = cStart; c < cEnd; ++c) {
8783     PetscInt *closure = NULL;
8784     PetscInt  coneSize, closureSize, cl, Nv = 0;
8785 
8786     PetscCall(DMPlexGetCellType(dm, c, &ct));
8787     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8788     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8789     if (interp == DMPLEX_INTERPOLATED_FULL) {
8790       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8791       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));
8792     }
8793     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8794     for (cl = 0; cl < closureSize * 2; cl += 2) {
8795       const PetscInt p = closure[cl];
8796       if ((p >= vStart) && (p < vEnd)) ++Nv;
8797     }
8798     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8799     /* Special Case: Tensor faces with identified vertices */
8800     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8801       PetscInt unsplit;
8802 
8803       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8804       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8805     }
8806     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));
8807   }
8808   PetscFunctionReturn(PETSC_SUCCESS);
8809 }
8810 
8811 /*@
8812   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8813 
8814   Collective
8815 
8816   Input Parameters:
8817 + dm - The `DMPLEX` object
8818 - cellHeight - Normally 0
8819 
8820   Level: developer
8821 
8822   Notes:
8823   This is a useful diagnostic when creating meshes programmatically.
8824   This routine is only relevant for meshes that are fully interpolated across all ranks.
8825   It will error out if a partially interpolated mesh is given on some rank.
8826   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8827 
8828   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8829 
8830 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8831 @*/
8832 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8833 {
8834   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8835   DMPlexInterpolatedFlag interpEnum;
8836 
8837   PetscFunctionBegin;
8838   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8839   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8840   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8841   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8842     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8843     PetscFunctionReturn(PETSC_SUCCESS);
8844   }
8845 
8846   PetscCall(DMGetDimension(dm, &dim));
8847   PetscCall(DMPlexGetDepth(dm, &depth));
8848   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8849   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8850     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8851     for (c = cStart; c < cEnd; ++c) {
8852       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8853       const DMPolytopeType *faceTypes;
8854       DMPolytopeType        ct;
8855       PetscInt              numFaces, coneSize, f;
8856       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8857 
8858       PetscCall(DMPlexGetCellType(dm, c, &ct));
8859       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8860       if (unsplit) continue;
8861       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8862       PetscCall(DMPlexGetCone(dm, c, &cone));
8863       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8864       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8865       for (cl = 0; cl < closureSize * 2; cl += 2) {
8866         const PetscInt p = closure[cl];
8867         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8868       }
8869       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8870       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);
8871       for (f = 0; f < numFaces; ++f) {
8872         DMPolytopeType fct;
8873         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8874 
8875         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8876         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8877         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8878           const PetscInt p = fclosure[cl];
8879           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8880         }
8881         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]);
8882         for (v = 0; v < fnumCorners; ++v) {
8883           if (fclosure[v] != faces[fOff + v]) {
8884             PetscInt v1;
8885 
8886             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8887             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8888             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8889             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8890             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8891             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]);
8892           }
8893         }
8894         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8895         fOff += faceSizes[f];
8896       }
8897       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8898       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8899     }
8900   }
8901   PetscFunctionReturn(PETSC_SUCCESS);
8902 }
8903 
8904 /*@
8905   DMPlexCheckGeometry - Check the geometry of mesh cells
8906 
8907   Input Parameter:
8908 . dm - The `DMPLEX` object
8909 
8910   Level: developer
8911 
8912   Notes:
8913   This is a useful diagnostic when creating meshes programmatically.
8914 
8915   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8916 
8917 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8918 @*/
8919 PetscErrorCode DMPlexCheckGeometry(DM dm)
8920 {
8921   Vec       coordinates;
8922   PetscReal detJ, J[9], refVol = 1.0;
8923   PetscReal vol;
8924   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8925 
8926   PetscFunctionBegin;
8927   PetscCall(DMGetDimension(dm, &dim));
8928   PetscCall(DMGetCoordinateDim(dm, &dE));
8929   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
8930   PetscCall(DMPlexGetDepth(dm, &depth));
8931   for (d = 0; d < dim; ++d) refVol *= 2.0;
8932   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8933   /* Make sure local coordinates are created, because that step is collective */
8934   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8935   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
8936   for (c = cStart; c < cEnd; ++c) {
8937     DMPolytopeType ct;
8938     PetscInt       unsplit;
8939     PetscBool      ignoreZeroVol = PETSC_FALSE;
8940 
8941     PetscCall(DMPlexGetCellType(dm, c, &ct));
8942     switch (ct) {
8943     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8944     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8945     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8946       ignoreZeroVol = PETSC_TRUE;
8947       break;
8948     default:
8949       break;
8950     }
8951     switch (ct) {
8952     case DM_POLYTOPE_TRI_PRISM:
8953     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8954     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8955     case DM_POLYTOPE_PYRAMID:
8956       continue;
8957     default:
8958       break;
8959     }
8960     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8961     if (unsplit) continue;
8962     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8963     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);
8964     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8965     /* This should work with periodicity since DG coordinates should be used */
8966     if (depth > 1) {
8967       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8968       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);
8969       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8970     }
8971   }
8972   PetscFunctionReturn(PETSC_SUCCESS);
8973 }
8974 
8975 /*@
8976   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
8977 
8978   Collective
8979 
8980   Input Parameters:
8981 + dm - The `DMPLEX` object
8982 . pointSF - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
8983 - allowExtraRoots - Flag to allow extra points not present in the `DM`
8984 
8985   Level: developer
8986 
8987   Notes:
8988   This is mainly intended for debugging/testing purposes.
8989 
8990   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8991 
8992   Extra roots can come from priodic cuts, where additional points appear on the boundary
8993 
8994 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
8995 @*/
8996 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
8997 {
8998   PetscInt           l, nleaves, nroots, overlap;
8999   const PetscInt    *locals;
9000   const PetscSFNode *remotes;
9001   PetscBool          distributed;
9002   MPI_Comm           comm;
9003   PetscMPIInt        rank;
9004 
9005   PetscFunctionBegin;
9006   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9007   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9008   else pointSF = dm->sf;
9009   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9010   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9011   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9012   {
9013     PetscMPIInt mpiFlag;
9014 
9015     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9016     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9017   }
9018   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9019   PetscCall(DMPlexIsDistributed(dm, &distributed));
9020   if (!distributed) {
9021     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);
9022     PetscFunctionReturn(PETSC_SUCCESS);
9023   }
9024   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);
9025   PetscCall(DMPlexGetOverlap(dm, &overlap));
9026 
9027   /* Check SF graph is compatible with DMPlex chart */
9028   {
9029     PetscInt pStart, pEnd, maxLeaf;
9030 
9031     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9032     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9033     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9034     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9035   }
9036 
9037   /* Check Point SF has no local points referenced */
9038   for (l = 0; l < nleaves; l++) {
9039     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);
9040   }
9041 
9042   /* Check there are no cells in interface */
9043   if (!overlap) {
9044     PetscInt cellHeight, cStart, cEnd;
9045 
9046     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9047     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9048     for (l = 0; l < nleaves; ++l) {
9049       const PetscInt point = locals ? locals[l] : l;
9050 
9051       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9052     }
9053   }
9054 
9055   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9056   {
9057     const PetscInt *rootdegree;
9058 
9059     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9060     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9061     for (l = 0; l < nleaves; ++l) {
9062       const PetscInt  point = locals ? locals[l] : l;
9063       const PetscInt *cone;
9064       PetscInt        coneSize, c, idx;
9065 
9066       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9067       PetscCall(DMPlexGetCone(dm, point, &cone));
9068       for (c = 0; c < coneSize; ++c) {
9069         if (!rootdegree[cone[c]]) {
9070           if (locals) {
9071             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9072           } else {
9073             idx = (cone[c] < nleaves) ? cone[c] : -1;
9074           }
9075           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9076         }
9077       }
9078     }
9079   }
9080   PetscFunctionReturn(PETSC_SUCCESS);
9081 }
9082 
9083 /*@
9084   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9085 
9086   Input Parameter:
9087 . dm - The `DMPLEX` object
9088 
9089   Level: developer
9090 
9091   Notes:
9092   This is a useful diagnostic when creating meshes programmatically.
9093 
9094   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9095 
9096   Currently does not include `DMPlexCheckCellShape()`.
9097 
9098 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9099 @*/
9100 PetscErrorCode DMPlexCheck(DM dm)
9101 {
9102   PetscInt cellHeight;
9103 
9104   PetscFunctionBegin;
9105   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9106   PetscCall(DMPlexCheckSymmetry(dm));
9107   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9108   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9109   PetscCall(DMPlexCheckGeometry(dm));
9110   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9111   PetscCall(DMPlexCheckInterfaceCones(dm));
9112   PetscFunctionReturn(PETSC_SUCCESS);
9113 }
9114 
9115 typedef struct cell_stats {
9116   PetscReal min, max, sum, squaresum;
9117   PetscInt  count;
9118 } cell_stats_t;
9119 
9120 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9121 {
9122   PetscInt i, N = *len;
9123 
9124   for (i = 0; i < N; i++) {
9125     cell_stats_t *A = (cell_stats_t *)a;
9126     cell_stats_t *B = (cell_stats_t *)b;
9127 
9128     B->min = PetscMin(A->min, B->min);
9129     B->max = PetscMax(A->max, B->max);
9130     B->sum += A->sum;
9131     B->squaresum += A->squaresum;
9132     B->count += A->count;
9133   }
9134 }
9135 
9136 /*@
9137   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9138 
9139   Collective
9140 
9141   Input Parameters:
9142 + dm        - The `DMPLEX` object
9143 . output    - If true, statistics will be displayed on `stdout`
9144 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9145 
9146   Level: developer
9147 
9148   Notes:
9149   This is mainly intended for debugging/testing purposes.
9150 
9151   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9152 
9153 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9154 @*/
9155 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9156 {
9157   DM           dmCoarse;
9158   cell_stats_t stats, globalStats;
9159   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9160   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9161   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9162   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9163   PetscMPIInt  rank, size;
9164 
9165   PetscFunctionBegin;
9166   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9167   stats.min = PETSC_MAX_REAL;
9168   stats.max = PETSC_MIN_REAL;
9169   stats.sum = stats.squaresum = 0.;
9170   stats.count                 = 0;
9171 
9172   PetscCallMPI(MPI_Comm_size(comm, &size));
9173   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9174   PetscCall(DMGetCoordinateDim(dm, &cdim));
9175   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9176   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9177   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9178   for (c = cStart; c < cEnd; c++) {
9179     PetscInt  i;
9180     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9181 
9182     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9183     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9184     for (i = 0; i < PetscSqr(cdim); ++i) {
9185       frobJ += J[i] * J[i];
9186       frobInvJ += invJ[i] * invJ[i];
9187     }
9188     cond2 = frobJ * frobInvJ;
9189     cond  = PetscSqrtReal(cond2);
9190 
9191     stats.min = PetscMin(stats.min, cond);
9192     stats.max = PetscMax(stats.max, cond);
9193     stats.sum += cond;
9194     stats.squaresum += cond2;
9195     stats.count++;
9196     if (output && cond > limit) {
9197       PetscSection coordSection;
9198       Vec          coordsLocal;
9199       PetscScalar *coords = NULL;
9200       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9201 
9202       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9203       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9204       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9205       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9206       for (i = 0; i < Nv / cdim; ++i) {
9207         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9208         for (d = 0; d < cdim; ++d) {
9209           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9210           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9211         }
9212         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9213       }
9214       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9215       for (cl = 0; cl < clSize * 2; cl += 2) {
9216         const PetscInt edge = closure[cl];
9217 
9218         if ((edge >= eStart) && (edge < eEnd)) {
9219           PetscReal len;
9220 
9221           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9222           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9223         }
9224       }
9225       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9226       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9227     }
9228   }
9229   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9230 
9231   if (size > 1) {
9232     PetscMPIInt  blockLengths[2] = {4, 1};
9233     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9234     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9235     MPI_Op       statReduce;
9236 
9237     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9238     PetscCallMPI(MPI_Type_commit(&statType));
9239     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9240     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9241     PetscCallMPI(MPI_Op_free(&statReduce));
9242     PetscCallMPI(MPI_Type_free(&statType));
9243   } else {
9244     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9245   }
9246   if (rank == 0) {
9247     count = globalStats.count;
9248     min   = globalStats.min;
9249     max   = globalStats.max;
9250     mean  = globalStats.sum / globalStats.count;
9251     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9252   }
9253 
9254   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));
9255   PetscCall(PetscFree2(J, invJ));
9256 
9257   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9258   if (dmCoarse) {
9259     PetscBool isplex;
9260 
9261     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9262     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9263   }
9264   PetscFunctionReturn(PETSC_SUCCESS);
9265 }
9266 
9267 /*@
9268   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9269   orthogonal quality below given tolerance.
9270 
9271   Collective
9272 
9273   Input Parameters:
9274 + dm   - The `DMPLEX` object
9275 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9276 - atol - [0, 1] Absolute tolerance for tagging cells.
9277 
9278   Output Parameters:
9279 + OrthQual      - `Vec` containing orthogonal quality per cell
9280 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9281 
9282   Options Database Keys:
9283 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9284 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9285 
9286   Level: intermediate
9287 
9288   Notes:
9289   Orthogonal quality is given by the following formula:
9290 
9291   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9292 
9293   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
9294   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9295   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9296   calculating the cosine of the angle between these vectors.
9297 
9298   Orthogonal quality ranges from 1 (best) to 0 (worst).
9299 
9300   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9301   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9302 
9303   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9304 
9305 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9306 @*/
9307 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9308 {
9309   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9310   PetscInt              *idx;
9311   PetscScalar           *oqVals;
9312   const PetscScalar     *cellGeomArr, *faceGeomArr;
9313   PetscReal             *ci, *fi, *Ai;
9314   MPI_Comm               comm;
9315   Vec                    cellgeom, facegeom;
9316   DM                     dmFace, dmCell;
9317   IS                     glob;
9318   ISLocalToGlobalMapping ltog;
9319   PetscViewer            vwr;
9320 
9321   PetscFunctionBegin;
9322   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9323   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9324   PetscValidPointer(OrthQual, 4);
9325   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9326   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9327   PetscCall(DMGetDimension(dm, &nc));
9328   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9329   {
9330     DMPlexInterpolatedFlag interpFlag;
9331 
9332     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9333     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9334       PetscMPIInt rank;
9335 
9336       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9337       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9338     }
9339   }
9340   if (OrthQualLabel) {
9341     PetscValidPointer(OrthQualLabel, 5);
9342     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9343     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9344   } else {
9345     *OrthQualLabel = NULL;
9346   }
9347   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9348   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9349   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9350   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9351   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9352   PetscCall(VecCreate(comm, OrthQual));
9353   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9354   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9355   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9356   PetscCall(VecSetUp(*OrthQual));
9357   PetscCall(ISDestroy(&glob));
9358   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9359   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9360   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9361   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9362   PetscCall(VecGetDM(cellgeom, &dmCell));
9363   PetscCall(VecGetDM(facegeom, &dmFace));
9364   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9365   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9366     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9367     PetscInt         cellarr[2], *adj = NULL;
9368     PetscScalar     *cArr, *fArr;
9369     PetscReal        minvalc = 1.0, minvalf = 1.0;
9370     PetscFVCellGeom *cg;
9371 
9372     idx[cellIter] = cell - cStart;
9373     cellarr[0]    = cell;
9374     /* Make indexing into cellGeom easier */
9375     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9376     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9377     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9378     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9379     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9380       PetscInt         i;
9381       const PetscInt   neigh  = adj[cellneigh];
9382       PetscReal        normci = 0, normfi = 0, normai = 0;
9383       PetscFVCellGeom *cgneigh;
9384       PetscFVFaceGeom *fg;
9385 
9386       /* Don't count ourselves in the neighbor list */
9387       if (neigh == cell) continue;
9388       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9389       cellarr[1] = neigh;
9390       {
9391         PetscInt        numcovpts;
9392         const PetscInt *covpts;
9393 
9394         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9395         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9396         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9397       }
9398 
9399       /* Compute c_i, f_i and their norms */
9400       for (i = 0; i < nc; i++) {
9401         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9402         fi[i] = fg->centroid[i] - cg->centroid[i];
9403         Ai[i] = fg->normal[i];
9404         normci += PetscPowReal(ci[i], 2);
9405         normfi += PetscPowReal(fi[i], 2);
9406         normai += PetscPowReal(Ai[i], 2);
9407       }
9408       normci = PetscSqrtReal(normci);
9409       normfi = PetscSqrtReal(normfi);
9410       normai = PetscSqrtReal(normai);
9411 
9412       /* Normalize and compute for each face-cell-normal pair */
9413       for (i = 0; i < nc; i++) {
9414         ci[i] = ci[i] / normci;
9415         fi[i] = fi[i] / normfi;
9416         Ai[i] = Ai[i] / normai;
9417         /* PetscAbs because I don't know if normals are guaranteed to point out */
9418         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9419         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9420       }
9421       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9422       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9423     }
9424     PetscCall(PetscFree(adj));
9425     PetscCall(PetscFree2(cArr, fArr));
9426     /* Defer to cell if they're equal */
9427     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9428     if (OrthQualLabel) {
9429       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9430     }
9431   }
9432   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9433   PetscCall(VecAssemblyBegin(*OrthQual));
9434   PetscCall(VecAssemblyEnd(*OrthQual));
9435   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9436   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9437   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9438   if (OrthQualLabel) {
9439     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9440   }
9441   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9442   PetscCall(PetscViewerDestroy(&vwr));
9443   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9444   PetscFunctionReturn(PETSC_SUCCESS);
9445 }
9446 
9447 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9448  * interpolator construction */
9449 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9450 {
9451   PetscSection section, newSection, gsection;
9452   PetscSF      sf;
9453   PetscBool    hasConstraints, ghasConstraints;
9454 
9455   PetscFunctionBegin;
9456   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9457   PetscValidPointer(odm, 2);
9458   PetscCall(DMGetLocalSection(dm, &section));
9459   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9460   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9461   if (!ghasConstraints) {
9462     PetscCall(PetscObjectReference((PetscObject)dm));
9463     *odm = dm;
9464     PetscFunctionReturn(PETSC_SUCCESS);
9465   }
9466   PetscCall(DMClone(dm, odm));
9467   PetscCall(DMCopyFields(dm, *odm));
9468   PetscCall(DMGetLocalSection(*odm, &newSection));
9469   PetscCall(DMGetPointSF(*odm, &sf));
9470   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9471   PetscCall(DMSetGlobalSection(*odm, gsection));
9472   PetscCall(PetscSectionDestroy(&gsection));
9473   PetscFunctionReturn(PETSC_SUCCESS);
9474 }
9475 
9476 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9477 {
9478   DM        dmco, dmfo;
9479   Mat       interpo;
9480   Vec       rscale;
9481   Vec       cglobalo, clocal;
9482   Vec       fglobal, fglobalo, flocal;
9483   PetscBool regular;
9484 
9485   PetscFunctionBegin;
9486   PetscCall(DMGetFullDM(dmc, &dmco));
9487   PetscCall(DMGetFullDM(dmf, &dmfo));
9488   PetscCall(DMSetCoarseDM(dmfo, dmco));
9489   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9490   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9491   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9492   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9493   PetscCall(DMCreateLocalVector(dmc, &clocal));
9494   PetscCall(VecSet(cglobalo, 0.));
9495   PetscCall(VecSet(clocal, 0.));
9496   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9497   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9498   PetscCall(DMCreateLocalVector(dmf, &flocal));
9499   PetscCall(VecSet(fglobal, 0.));
9500   PetscCall(VecSet(fglobalo, 0.));
9501   PetscCall(VecSet(flocal, 0.));
9502   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9503   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9504   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9505   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9506   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9507   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9508   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9509   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9510   *shift = fglobal;
9511   PetscCall(VecDestroy(&flocal));
9512   PetscCall(VecDestroy(&fglobalo));
9513   PetscCall(VecDestroy(&clocal));
9514   PetscCall(VecDestroy(&cglobalo));
9515   PetscCall(VecDestroy(&rscale));
9516   PetscCall(MatDestroy(&interpo));
9517   PetscCall(DMDestroy(&dmfo));
9518   PetscCall(DMDestroy(&dmco));
9519   PetscFunctionReturn(PETSC_SUCCESS);
9520 }
9521 
9522 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9523 {
9524   PetscObject shifto;
9525   Vec         shift;
9526 
9527   PetscFunctionBegin;
9528   if (!interp) {
9529     Vec rscale;
9530 
9531     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9532     PetscCall(VecDestroy(&rscale));
9533   } else {
9534     PetscCall(PetscObjectReference((PetscObject)interp));
9535   }
9536   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9537   if (!shifto) {
9538     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9539     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9540     shifto = (PetscObject)shift;
9541     PetscCall(VecDestroy(&shift));
9542   }
9543   shift = (Vec)shifto;
9544   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9545   PetscCall(VecAXPY(fineSol, 1.0, shift));
9546   PetscCall(MatDestroy(&interp));
9547   PetscFunctionReturn(PETSC_SUCCESS);
9548 }
9549 
9550 /* Pointwise interpolation
9551      Just code FEM for now
9552      u^f = I u^c
9553      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9554      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9555      I_{ij} = psi^f_i phi^c_j
9556 */
9557 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9558 {
9559   PetscSection gsc, gsf;
9560   PetscInt     m, n;
9561   void        *ctx;
9562   DM           cdm;
9563   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9564 
9565   PetscFunctionBegin;
9566   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9567   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9568   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9569   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9570 
9571   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9572   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9573   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9574   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9575   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9576 
9577   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9578   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9579   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9580   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9581   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9582   if (scaling) {
9583     /* Use naive scaling */
9584     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9585   }
9586   PetscFunctionReturn(PETSC_SUCCESS);
9587 }
9588 
9589 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9590 {
9591   VecScatter ctx;
9592 
9593   PetscFunctionBegin;
9594   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9595   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9596   PetscCall(VecScatterDestroy(&ctx));
9597   PetscFunctionReturn(PETSC_SUCCESS);
9598 }
9599 
9600 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[])
9601 {
9602   const PetscInt Nc = uOff[1] - uOff[0];
9603   PetscInt       c;
9604   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9605 }
9606 
9607 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9608 {
9609   DM           dmc;
9610   PetscDS      ds;
9611   Vec          ones, locmass;
9612   IS           cellIS;
9613   PetscFormKey key;
9614   PetscInt     depth;
9615 
9616   PetscFunctionBegin;
9617   PetscCall(DMClone(dm, &dmc));
9618   PetscCall(DMCopyDisc(dm, dmc));
9619   PetscCall(DMGetDS(dmc, &ds));
9620   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9621   PetscCall(DMCreateGlobalVector(dmc, mass));
9622   PetscCall(DMGetLocalVector(dmc, &ones));
9623   PetscCall(DMGetLocalVector(dmc, &locmass));
9624   PetscCall(DMPlexGetDepth(dmc, &depth));
9625   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9626   PetscCall(VecSet(locmass, 0.0));
9627   PetscCall(VecSet(ones, 1.0));
9628   key.label = NULL;
9629   key.value = 0;
9630   key.field = 0;
9631   key.part  = 0;
9632   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9633   PetscCall(ISDestroy(&cellIS));
9634   PetscCall(VecSet(*mass, 0.0));
9635   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9636   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9637   PetscCall(DMRestoreLocalVector(dmc, &ones));
9638   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9639   PetscCall(DMDestroy(&dmc));
9640   PetscFunctionReturn(PETSC_SUCCESS);
9641 }
9642 
9643 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9644 {
9645   PetscSection gsc, gsf;
9646   PetscInt     m, n;
9647   void        *ctx;
9648   DM           cdm;
9649   PetscBool    regular;
9650 
9651   PetscFunctionBegin;
9652   if (dmFine == dmCoarse) {
9653     DM            dmc;
9654     PetscDS       ds;
9655     PetscWeakForm wf;
9656     Vec           u;
9657     IS            cellIS;
9658     PetscFormKey  key;
9659     PetscInt      depth;
9660 
9661     PetscCall(DMClone(dmFine, &dmc));
9662     PetscCall(DMCopyDisc(dmFine, dmc));
9663     PetscCall(DMGetDS(dmc, &ds));
9664     PetscCall(PetscDSGetWeakForm(ds, &wf));
9665     PetscCall(PetscWeakFormClear(wf));
9666     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9667     PetscCall(DMCreateMatrix(dmc, mass));
9668     PetscCall(DMGetLocalVector(dmc, &u));
9669     PetscCall(DMPlexGetDepth(dmc, &depth));
9670     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9671     PetscCall(MatZeroEntries(*mass));
9672     key.label = NULL;
9673     key.value = 0;
9674     key.field = 0;
9675     key.part  = 0;
9676     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9677     PetscCall(ISDestroy(&cellIS));
9678     PetscCall(DMRestoreLocalVector(dmc, &u));
9679     PetscCall(DMDestroy(&dmc));
9680   } else {
9681     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9682     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9683     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9684     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9685 
9686     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9687     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9688     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9689     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9690 
9691     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9692     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9693     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9694     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9695   }
9696   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9697   PetscFunctionReturn(PETSC_SUCCESS);
9698 }
9699 
9700 /*@
9701   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9702 
9703   Input Parameter:
9704 . dm - The `DMPLEX` object
9705 
9706   Output Parameter:
9707 . regular - The flag
9708 
9709   Level: intermediate
9710 
9711 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9712 @*/
9713 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9714 {
9715   PetscFunctionBegin;
9716   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9717   PetscValidBoolPointer(regular, 2);
9718   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9719   PetscFunctionReturn(PETSC_SUCCESS);
9720 }
9721 
9722 /*@
9723   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9724 
9725   Input Parameters:
9726 + dm - The `DMPLEX` object
9727 - regular - The flag
9728 
9729   Level: intermediate
9730 
9731 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9732 @*/
9733 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9734 {
9735   PetscFunctionBegin;
9736   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9737   ((DM_Plex *)dm->data)->regularRefinement = regular;
9738   PetscFunctionReturn(PETSC_SUCCESS);
9739 }
9740 
9741 /*@
9742   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9743   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9744 
9745   Not Collective
9746 
9747   Input Parameter:
9748 . dm - The `DMPLEX` object
9749 
9750   Output Parameters:
9751 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9752 - anchorIS - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9753 
9754   Level: intermediate
9755 
9756 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9757 @*/
9758 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9759 {
9760   DM_Plex *plex = (DM_Plex *)dm->data;
9761 
9762   PetscFunctionBegin;
9763   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9764   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9765   if (anchorSection) *anchorSection = plex->anchorSection;
9766   if (anchorIS) *anchorIS = plex->anchorIS;
9767   PetscFunctionReturn(PETSC_SUCCESS);
9768 }
9769 
9770 /*@
9771   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9772   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9773   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9774 
9775   Collective
9776 
9777   Input Parameters:
9778 + dm - The `DMPLEX` object
9779 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9780                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9781 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9782 
9783   Level: intermediate
9784 
9785   Notes:
9786   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9787   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9788 
9789   The reference counts of `anchorSection` and `anchorIS` are incremented.
9790 
9791 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9792 @*/
9793 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9794 {
9795   DM_Plex    *plex = (DM_Plex *)dm->data;
9796   PetscMPIInt result;
9797 
9798   PetscFunctionBegin;
9799   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9800   if (anchorSection) {
9801     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9802     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9803     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9804   }
9805   if (anchorIS) {
9806     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9807     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9808     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9809   }
9810 
9811   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9812   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9813   plex->anchorSection = anchorSection;
9814 
9815   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9816   PetscCall(ISDestroy(&plex->anchorIS));
9817   plex->anchorIS = anchorIS;
9818 
9819   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9820     PetscInt        size, a, pStart, pEnd;
9821     const PetscInt *anchors;
9822 
9823     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9824     PetscCall(ISGetLocalSize(anchorIS, &size));
9825     PetscCall(ISGetIndices(anchorIS, &anchors));
9826     for (a = 0; a < size; a++) {
9827       PetscInt p;
9828 
9829       p = anchors[a];
9830       if (p >= pStart && p < pEnd) {
9831         PetscInt dof;
9832 
9833         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9834         if (dof) {
9835           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9836           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9837         }
9838       }
9839     }
9840     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9841   }
9842   /* reset the generic constraints */
9843   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9844   PetscFunctionReturn(PETSC_SUCCESS);
9845 }
9846 
9847 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9848 {
9849   PetscSection anchorSection;
9850   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9851 
9852   PetscFunctionBegin;
9853   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9854   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9855   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9856   PetscCall(PetscSectionGetNumFields(section, &numFields));
9857   if (numFields) {
9858     PetscInt f;
9859     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9860 
9861     for (f = 0; f < numFields; f++) {
9862       PetscInt numComp;
9863 
9864       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9865       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9866     }
9867   }
9868   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9869   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9870   pStart = PetscMax(pStart, sStart);
9871   pEnd   = PetscMin(pEnd, sEnd);
9872   pEnd   = PetscMax(pStart, pEnd);
9873   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9874   for (p = pStart; p < pEnd; p++) {
9875     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9876     if (dof) {
9877       PetscCall(PetscSectionGetDof(section, p, &dof));
9878       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9879       for (f = 0; f < numFields; f++) {
9880         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9881         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9882       }
9883     }
9884   }
9885   PetscCall(PetscSectionSetUp(*cSec));
9886   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9887   PetscFunctionReturn(PETSC_SUCCESS);
9888 }
9889 
9890 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9891 {
9892   PetscSection    aSec;
9893   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9894   const PetscInt *anchors;
9895   PetscInt        numFields, f;
9896   IS              aIS;
9897   MatType         mtype;
9898   PetscBool       iscuda, iskokkos;
9899 
9900   PetscFunctionBegin;
9901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9902   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9903   PetscCall(PetscSectionGetStorageSize(section, &n));
9904   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9905   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9906   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9907   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9908   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9909   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9910   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9911   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9912   else mtype = MATSEQAIJ;
9913   PetscCall(MatSetType(*cMat, mtype));
9914   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9915   PetscCall(ISGetIndices(aIS, &anchors));
9916   /* cSec will be a subset of aSec and section */
9917   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9918   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9919   PetscCall(PetscMalloc1(m + 1, &i));
9920   i[0] = 0;
9921   PetscCall(PetscSectionGetNumFields(section, &numFields));
9922   for (p = pStart; p < pEnd; p++) {
9923     PetscInt rDof, rOff, r;
9924 
9925     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9926     if (!rDof) continue;
9927     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9928     if (numFields) {
9929       for (f = 0; f < numFields; f++) {
9930         annz = 0;
9931         for (r = 0; r < rDof; r++) {
9932           a = anchors[rOff + r];
9933           if (a < sStart || a >= sEnd) continue;
9934           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9935           annz += aDof;
9936         }
9937         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9938         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9939         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9940       }
9941     } else {
9942       annz = 0;
9943       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9944       for (q = 0; q < dof; q++) {
9945         a = anchors[rOff + q];
9946         if (a < sStart || a >= sEnd) continue;
9947         PetscCall(PetscSectionGetDof(section, a, &aDof));
9948         annz += aDof;
9949       }
9950       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9951       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9952       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9953     }
9954   }
9955   nnz = i[m];
9956   PetscCall(PetscMalloc1(nnz, &j));
9957   offset = 0;
9958   for (p = pStart; p < pEnd; p++) {
9959     if (numFields) {
9960       for (f = 0; f < numFields; f++) {
9961         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9962         for (q = 0; q < dof; q++) {
9963           PetscInt rDof, rOff, r;
9964           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9965           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9966           for (r = 0; r < rDof; r++) {
9967             PetscInt s;
9968 
9969             a = anchors[rOff + r];
9970             if (a < sStart || a >= sEnd) continue;
9971             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9972             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9973             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9974           }
9975         }
9976       }
9977     } else {
9978       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9979       for (q = 0; q < dof; q++) {
9980         PetscInt rDof, rOff, r;
9981         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9982         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9983         for (r = 0; r < rDof; r++) {
9984           PetscInt s;
9985 
9986           a = anchors[rOff + r];
9987           if (a < sStart || a >= sEnd) continue;
9988           PetscCall(PetscSectionGetDof(section, a, &aDof));
9989           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9990           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9991         }
9992       }
9993     }
9994   }
9995   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9996   PetscCall(PetscFree(i));
9997   PetscCall(PetscFree(j));
9998   PetscCall(ISRestoreIndices(aIS, &anchors));
9999   PetscFunctionReturn(PETSC_SUCCESS);
10000 }
10001 
10002 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10003 {
10004   DM_Plex     *plex = (DM_Plex *)dm->data;
10005   PetscSection anchorSection, section, cSec;
10006   Mat          cMat;
10007 
10008   PetscFunctionBegin;
10009   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10010   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10011   if (anchorSection) {
10012     PetscInt Nf;
10013 
10014     PetscCall(DMGetLocalSection(dm, &section));
10015     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10016     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10017     PetscCall(DMGetNumFields(dm, &Nf));
10018     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10019     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10020     PetscCall(PetscSectionDestroy(&cSec));
10021     PetscCall(MatDestroy(&cMat));
10022   }
10023   PetscFunctionReturn(PETSC_SUCCESS);
10024 }
10025 
10026 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10027 {
10028   IS           subis;
10029   PetscSection section, subsection;
10030 
10031   PetscFunctionBegin;
10032   PetscCall(DMGetLocalSection(dm, &section));
10033   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10034   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10035   /* Create subdomain */
10036   PetscCall(DMPlexFilter(dm, label, value, subdm));
10037   /* Create submodel */
10038   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10039   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10040   PetscCall(DMSetLocalSection(*subdm, subsection));
10041   PetscCall(PetscSectionDestroy(&subsection));
10042   PetscCall(DMCopyDisc(dm, *subdm));
10043   /* Create map from submodel to global model */
10044   if (is) {
10045     PetscSection    sectionGlobal, subsectionGlobal;
10046     IS              spIS;
10047     const PetscInt *spmap;
10048     PetscInt       *subIndices;
10049     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10050     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10051 
10052     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10053     PetscCall(ISGetIndices(spIS, &spmap));
10054     PetscCall(PetscSectionGetNumFields(section, &Nf));
10055     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10056     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10057     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10058     for (p = pStart; p < pEnd; ++p) {
10059       PetscInt gdof, pSubSize = 0;
10060 
10061       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10062       if (gdof > 0) {
10063         for (f = 0; f < Nf; ++f) {
10064           PetscInt fdof, fcdof;
10065 
10066           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10067           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10068           pSubSize += fdof - fcdof;
10069         }
10070         subSize += pSubSize;
10071         if (pSubSize) {
10072           if (bs < 0) {
10073             bs = pSubSize;
10074           } else if (bs != pSubSize) {
10075             /* Layout does not admit a pointwise block size */
10076             bs = 1;
10077           }
10078         }
10079       }
10080     }
10081     /* Must have same blocksize on all procs (some might have no points) */
10082     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10083     bsLocal[1] = bs;
10084     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10085     if (bsMinMax[0] != bsMinMax[1]) {
10086       bs = 1;
10087     } else {
10088       bs = bsMinMax[0];
10089     }
10090     PetscCall(PetscMalloc1(subSize, &subIndices));
10091     for (p = pStart; p < pEnd; ++p) {
10092       PetscInt gdof, goff;
10093 
10094       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10095       if (gdof > 0) {
10096         const PetscInt point = spmap[p];
10097 
10098         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10099         for (f = 0; f < Nf; ++f) {
10100           PetscInt fdof, fcdof, fc, f2, poff = 0;
10101 
10102           /* Can get rid of this loop by storing field information in the global section */
10103           for (f2 = 0; f2 < f; ++f2) {
10104             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10105             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10106             poff += fdof - fcdof;
10107           }
10108           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10109           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10110           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10111         }
10112       }
10113     }
10114     PetscCall(ISRestoreIndices(spIS, &spmap));
10115     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10116     if (bs > 1) {
10117       /* We need to check that the block size does not come from non-contiguous fields */
10118       PetscInt i, j, set = 1;
10119       for (i = 0; i < subSize; i += bs) {
10120         for (j = 0; j < bs; ++j) {
10121           if (subIndices[i + j] != subIndices[i] + j) {
10122             set = 0;
10123             break;
10124           }
10125         }
10126       }
10127       if (set) PetscCall(ISSetBlockSize(*is, bs));
10128     }
10129     /* Attach nullspace */
10130     for (f = 0; f < Nf; ++f) {
10131       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10132       if ((*subdm)->nullspaceConstructors[f]) break;
10133     }
10134     if (f < Nf) {
10135       MatNullSpace nullSpace;
10136       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10137 
10138       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10139       PetscCall(MatNullSpaceDestroy(&nullSpace));
10140     }
10141   }
10142   PetscFunctionReturn(PETSC_SUCCESS);
10143 }
10144 
10145 /*@
10146   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10147 
10148   Input Parameters:
10149 + dm - The `DM`
10150 - dummy - unused argument
10151 
10152   Options Database Key:
10153 . -dm_plex_monitor_throughput - Activate the monitor
10154 
10155   Level: developer
10156 
10157 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10158 @*/
10159 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10160 {
10161 #if defined(PETSC_USE_LOG)
10162   PetscStageLog      stageLog;
10163   PetscLogEvent      event;
10164   PetscLogStage      stage;
10165   PetscEventPerfInfo eventInfo;
10166   PetscReal          cellRate, flopRate;
10167   PetscInt           cStart, cEnd, Nf, N;
10168   const char        *name;
10169 #endif
10170 
10171   PetscFunctionBegin;
10172   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10173 #if defined(PETSC_USE_LOG)
10174   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10175   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10176   PetscCall(DMGetNumFields(dm, &Nf));
10177   PetscCall(PetscLogGetStageLog(&stageLog));
10178   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10179   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10180   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10181   N        = (cEnd - cStart) * Nf * eventInfo.count;
10182   flopRate = eventInfo.flops / eventInfo.time;
10183   cellRate = N / eventInfo.time;
10184   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)));
10185 #else
10186   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10187 #endif
10188   PetscFunctionReturn(PETSC_SUCCESS);
10189 }
10190