xref: /petsc/src/dm/impls/plex/plex.c (revision 1cc06b555e92f8ec64db10330b8bbd830e5bc876)
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;
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   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3211   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);
3212   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);
3213   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3214   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
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   mesh->cones[off + conePos] = conePoint;
3217   PetscFunctionReturn(PETSC_SUCCESS);
3218 }
3219 
3220 /*@
3221   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3222 
3223   Not Collective
3224 
3225   Input Parameters:
3226 + mesh - The `DMPLEX`
3227 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3228 . conePos - The local index in the cone where the point should be put
3229 - coneOrientation - The point orientation to insert
3230 
3231   Level: beginner
3232 
3233   Note:
3234   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3235 
3236 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3237 @*/
3238 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3239 {
3240   DM_Plex *mesh = (DM_Plex *)dm->data;
3241   PetscInt pStart, pEnd;
3242   PetscInt dof, off;
3243 
3244   PetscFunctionBegin;
3245   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3246   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3247   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);
3248   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3249   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3250   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);
3251   mesh->coneOrientations[off + conePos] = coneOrientation;
3252   PetscFunctionReturn(PETSC_SUCCESS);
3253 }
3254 
3255 /*@C
3256   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3257 
3258   Not collective
3259 
3260   Input Parameters:
3261 + dm - The DMPlex
3262 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3263 
3264   Output Parameters:
3265 + cone - An array of points which are on the in-edges for point `p`
3266 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3267         integer giving the prescription for cone traversal.
3268 
3269   Level: beginner
3270 
3271   Notes:
3272   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3273   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3274   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3275   with the identity.
3276 
3277   Fortran Notes:
3278   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3279   `DMPlexRestoreCone()` is not needed/available in C.
3280 
3281 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3282 @*/
3283 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3284 {
3285   DM_Plex *mesh = (DM_Plex *)dm->data;
3286 
3287   PetscFunctionBegin;
3288   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3289   if (mesh->tr) {
3290     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3291   } else {
3292     PetscInt off;
3293     if (PetscDefined(USE_DEBUG)) {
3294       PetscInt dof;
3295       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3296       if (dof) {
3297         if (cone) PetscValidPointer(cone, 3);
3298         if (ornt) PetscValidPointer(ornt, 4);
3299       }
3300     }
3301     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3302     if (cone) *cone = &mesh->cones[off];
3303     if (ornt) *ornt = &mesh->coneOrientations[off];
3304   }
3305   PetscFunctionReturn(PETSC_SUCCESS);
3306 }
3307 
3308 /*@C
3309   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3310 
3311   Not Collective
3312 
3313   Input Parameters:
3314 + dm - The DMPlex
3315 . p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3316 . cone - An array of points which are on the in-edges for point p
3317 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3318         integer giving the prescription for cone traversal.
3319 
3320   Level: beginner
3321 
3322   Notes:
3323   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3324   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3325   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3326   with the identity.
3327 
3328   Fortran Note:
3329   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3330   `DMPlexRestoreCone()` is not needed/available in C.
3331 
3332 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3333 @*/
3334 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3335 {
3336   DM_Plex *mesh = (DM_Plex *)dm->data;
3337 
3338   PetscFunctionBegin;
3339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3340   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3341   PetscFunctionReturn(PETSC_SUCCESS);
3342 }
3343 
3344 /*@
3345   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3346 
3347   Not Collective
3348 
3349   Input Parameters:
3350 + mesh - The `DMPLEX`
3351 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3352 
3353   Output Parameter:
3354 . size - The support size for point `p`
3355 
3356   Level: beginner
3357 
3358 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3359 @*/
3360 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3361 {
3362   DM_Plex *mesh = (DM_Plex *)dm->data;
3363 
3364   PetscFunctionBegin;
3365   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3366   PetscValidIntPointer(size, 3);
3367   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3368   PetscFunctionReturn(PETSC_SUCCESS);
3369 }
3370 
3371 /*@
3372   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3373 
3374   Not Collective
3375 
3376   Input Parameters:
3377 + mesh - The `DMPLEX`
3378 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3379 - size - The support size for point `p`
3380 
3381   Level: beginner
3382 
3383   Note:
3384   This should be called after `DMPlexSetChart()`.
3385 
3386 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3387 @*/
3388 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3389 {
3390   DM_Plex *mesh = (DM_Plex *)dm->data;
3391 
3392   PetscFunctionBegin;
3393   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3394   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3395   PetscFunctionReturn(PETSC_SUCCESS);
3396 }
3397 
3398 /*@C
3399   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3400 
3401   Not Collective
3402 
3403   Input Parameters:
3404 + mesh - The `DMPLEX`
3405 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3406 
3407   Output Parameter:
3408 . support - An array of points which are on the out-edges for point `p`
3409 
3410   Level: beginner
3411 
3412   Fortran Note:
3413   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3414   `DMPlexRestoreSupport()` is not needed/available in C.
3415 
3416 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3417 @*/
3418 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3419 {
3420   DM_Plex *mesh = (DM_Plex *)dm->data;
3421   PetscInt off;
3422 
3423   PetscFunctionBegin;
3424   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3425   PetscValidPointer(support, 3);
3426   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3427   *support = &mesh->supports[off];
3428   PetscFunctionReturn(PETSC_SUCCESS);
3429 }
3430 
3431 /*@
3432   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3433 
3434   Not Collective
3435 
3436   Input Parameters:
3437 + mesh - The `DMPLEX`
3438 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3439 - support - An array of points which are on the out-edges for point `p`
3440 
3441   Level: beginner
3442 
3443   Note:
3444   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3445 
3446 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3447 @*/
3448 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3449 {
3450   DM_Plex *mesh = (DM_Plex *)dm->data;
3451   PetscInt pStart, pEnd;
3452   PetscInt dof, off, c;
3453 
3454   PetscFunctionBegin;
3455   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3456   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3457   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3458   if (dof) PetscValidIntPointer(support, 3);
3459   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3460   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);
3461   for (c = 0; c < dof; ++c) {
3462     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);
3463     mesh->supports[off + c] = support[c];
3464   }
3465   PetscFunctionReturn(PETSC_SUCCESS);
3466 }
3467 
3468 /*@
3469   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3470 
3471   Not Collective
3472 
3473   Input Parameters:
3474 + mesh - The `DMPLEX`
3475 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3476 . supportPos - The local index in the cone where the point should be put
3477 - supportPoint - The mesh point to insert
3478 
3479   Level: beginner
3480 
3481 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3482 @*/
3483 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3484 {
3485   DM_Plex *mesh = (DM_Plex *)dm->data;
3486   PetscInt pStart, pEnd;
3487   PetscInt dof, off;
3488 
3489   PetscFunctionBegin;
3490   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3491   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3492   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3493   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3494   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);
3495   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);
3496   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);
3497   mesh->supports[off + supportPos] = supportPoint;
3498   PetscFunctionReturn(PETSC_SUCCESS);
3499 }
3500 
3501 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3502 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3503 {
3504   switch (ct) {
3505   case DM_POLYTOPE_SEGMENT:
3506     if (o == -1) return -2;
3507     break;
3508   case DM_POLYTOPE_TRIANGLE:
3509     if (o == -3) return -1;
3510     if (o == -2) return -3;
3511     if (o == -1) return -2;
3512     break;
3513   case DM_POLYTOPE_QUADRILATERAL:
3514     if (o == -4) return -2;
3515     if (o == -3) return -1;
3516     if (o == -2) return -4;
3517     if (o == -1) return -3;
3518     break;
3519   default:
3520     return o;
3521   }
3522   return o;
3523 }
3524 
3525 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3526 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3527 {
3528   switch (ct) {
3529   case DM_POLYTOPE_SEGMENT:
3530     if ((o == -2) || (o == 1)) return -1;
3531     if (o == -1) return 0;
3532     break;
3533   case DM_POLYTOPE_TRIANGLE:
3534     if (o == -3) return -2;
3535     if (o == -2) return -1;
3536     if (o == -1) return -3;
3537     break;
3538   case DM_POLYTOPE_QUADRILATERAL:
3539     if (o == -4) return -2;
3540     if (o == -3) return -1;
3541     if (o == -2) return -4;
3542     if (o == -1) return -3;
3543     break;
3544   default:
3545     return o;
3546   }
3547   return o;
3548 }
3549 
3550 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3551 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3552 {
3553   PetscInt pStart, pEnd, p;
3554 
3555   PetscFunctionBegin;
3556   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3557   for (p = pStart; p < pEnd; ++p) {
3558     const PetscInt *cone, *ornt;
3559     PetscInt        coneSize, c;
3560 
3561     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3562     PetscCall(DMPlexGetCone(dm, p, &cone));
3563     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3564     for (c = 0; c < coneSize; ++c) {
3565       DMPolytopeType ct;
3566       const PetscInt o = ornt[c];
3567 
3568       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3569       switch (ct) {
3570       case DM_POLYTOPE_SEGMENT:
3571         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3572         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3573         break;
3574       case DM_POLYTOPE_TRIANGLE:
3575         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3576         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3577         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3578         break;
3579       case DM_POLYTOPE_QUADRILATERAL:
3580         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3581         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3582         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3583         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3584         break;
3585       default:
3586         break;
3587       }
3588     }
3589   }
3590   PetscFunctionReturn(PETSC_SUCCESS);
3591 }
3592 
3593 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3594 {
3595   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3596   PetscInt       *closure;
3597   const PetscInt *tmp = NULL, *tmpO = NULL;
3598   PetscInt        off = 0, tmpSize, t;
3599 
3600   PetscFunctionBeginHot;
3601   if (ornt) {
3602     PetscCall(DMPlexGetCellType(dm, p, &ct));
3603     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3604   }
3605   if (*points) {
3606     closure = *points;
3607   } else {
3608     PetscInt maxConeSize, maxSupportSize;
3609     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3610     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3611   }
3612   if (useCone) {
3613     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3614     PetscCall(DMPlexGetCone(dm, p, &tmp));
3615     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3616   } else {
3617     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3618     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3619   }
3620   if (ct == DM_POLYTOPE_UNKNOWN) {
3621     closure[off++] = p;
3622     closure[off++] = 0;
3623     for (t = 0; t < tmpSize; ++t) {
3624       closure[off++] = tmp[t];
3625       closure[off++] = tmpO ? tmpO[t] : 0;
3626     }
3627   } else {
3628     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3629 
3630     /* We assume that cells with a valid type have faces with a valid type */
3631     closure[off++] = p;
3632     closure[off++] = ornt;
3633     for (t = 0; t < tmpSize; ++t) {
3634       DMPolytopeType ft;
3635 
3636       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3637       closure[off++] = tmp[arr[t]];
3638       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3639     }
3640   }
3641   if (numPoints) *numPoints = tmpSize + 1;
3642   if (points) *points = closure;
3643   PetscFunctionReturn(PETSC_SUCCESS);
3644 }
3645 
3646 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3647 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3648 {
3649   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3650   const PetscInt *cone, *ornt;
3651   PetscInt       *pts, *closure = NULL;
3652   DMPolytopeType  ft;
3653   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3654   PetscInt        dim, coneSize, c, d, clSize, cl;
3655 
3656   PetscFunctionBeginHot;
3657   PetscCall(DMGetDimension(dm, &dim));
3658   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3659   PetscCall(DMPlexGetCone(dm, point, &cone));
3660   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3661   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3662   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3663   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3664   maxSize       = PetscMax(coneSeries, supportSeries);
3665   if (*points) {
3666     pts = *points;
3667   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3668   c        = 0;
3669   pts[c++] = point;
3670   pts[c++] = o;
3671   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3672   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3673   for (cl = 0; cl < clSize * 2; cl += 2) {
3674     pts[c++] = closure[cl];
3675     pts[c++] = closure[cl + 1];
3676   }
3677   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3678   for (cl = 0; cl < clSize * 2; cl += 2) {
3679     pts[c++] = closure[cl];
3680     pts[c++] = closure[cl + 1];
3681   }
3682   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3683   for (d = 2; d < coneSize; ++d) {
3684     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3685     pts[c++] = cone[arr[d * 2 + 0]];
3686     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3687   }
3688   if (dim >= 3) {
3689     for (d = 2; d < coneSize; ++d) {
3690       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3691       const PetscInt *fcone, *fornt;
3692       PetscInt        fconeSize, fc, i;
3693 
3694       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3695       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3696       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3697       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3698       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3699       for (fc = 0; fc < fconeSize; ++fc) {
3700         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3701         const PetscInt co = farr[fc * 2 + 1];
3702 
3703         for (i = 0; i < c; i += 2)
3704           if (pts[i] == cp) break;
3705         if (i == c) {
3706           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3707           pts[c++] = cp;
3708           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3709         }
3710       }
3711     }
3712   }
3713   *numPoints = c / 2;
3714   *points    = pts;
3715   PetscFunctionReturn(PETSC_SUCCESS);
3716 }
3717 
3718 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3719 {
3720   DMPolytopeType ct;
3721   PetscInt      *closure, *fifo;
3722   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3723   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3724   PetscInt       depth, maxSize;
3725 
3726   PetscFunctionBeginHot;
3727   PetscCall(DMPlexGetDepth(dm, &depth));
3728   if (depth == 1) {
3729     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3730     PetscFunctionReturn(PETSC_SUCCESS);
3731   }
3732   PetscCall(DMPlexGetCellType(dm, p, &ct));
3733   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3734   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3735     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3736     PetscFunctionReturn(PETSC_SUCCESS);
3737   }
3738   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3739   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3740   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3741   maxSize       = PetscMax(coneSeries, supportSeries);
3742   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3743   if (*points) {
3744     closure = *points;
3745   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3746   closure[closureSize++] = p;
3747   closure[closureSize++] = ornt;
3748   fifo[fifoSize++]       = p;
3749   fifo[fifoSize++]       = ornt;
3750   fifo[fifoSize++]       = ct;
3751   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3752   while (fifoSize - fifoStart) {
3753     const PetscInt       q    = fifo[fifoStart++];
3754     const PetscInt       o    = fifo[fifoStart++];
3755     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3756     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3757     const PetscInt      *tmp, *tmpO;
3758     PetscInt             tmpSize, t;
3759 
3760     if (PetscDefined(USE_DEBUG)) {
3761       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3762       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);
3763     }
3764     if (useCone) {
3765       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3766       PetscCall(DMPlexGetCone(dm, q, &tmp));
3767       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3768     } else {
3769       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3770       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3771       tmpO = NULL;
3772     }
3773     for (t = 0; t < tmpSize; ++t) {
3774       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3775       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3776       const PetscInt cp = tmp[ip];
3777       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3778       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3779       PetscInt       c;
3780 
3781       /* Check for duplicate */
3782       for (c = 0; c < closureSize; c += 2) {
3783         if (closure[c] == cp) break;
3784       }
3785       if (c == closureSize) {
3786         closure[closureSize++] = cp;
3787         closure[closureSize++] = co;
3788         fifo[fifoSize++]       = cp;
3789         fifo[fifoSize++]       = co;
3790         fifo[fifoSize++]       = ct;
3791       }
3792     }
3793   }
3794   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3795   if (numPoints) *numPoints = closureSize / 2;
3796   if (points) *points = closure;
3797   PetscFunctionReturn(PETSC_SUCCESS);
3798 }
3799 
3800 /*@C
3801   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3802 
3803   Not Collective
3804 
3805   Input Parameters:
3806 + dm      - The `DMPLEX`
3807 . p       - The mesh point
3808 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3809 
3810   Input/Output Parameter:
3811 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3812            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
3813 
3814   Output Parameter:
3815 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3816 
3817   Level: beginner
3818 
3819   Note:
3820   If using internal storage (points is `NULL` on input), each call overwrites the last output.
3821 
3822   Fortran Note:
3823   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
3824 
3825 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3826 @*/
3827 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3828 {
3829   PetscFunctionBeginHot;
3830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3831   if (numPoints) PetscValidIntPointer(numPoints, 4);
3832   if (points) PetscValidPointer(points, 5);
3833   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3834   PetscFunctionReturn(PETSC_SUCCESS);
3835 }
3836 
3837 /*@C
3838   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3839 
3840   Not Collective
3841 
3842   Input Parameters:
3843 + dm        - The `DMPLEX`
3844 . p         - The mesh point
3845 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3846 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3847 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3848 
3849   Level: beginner
3850 
3851   Note:
3852   If not using internal storage (points is not `NULL` on input), this call is unnecessary
3853 
3854 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3855 @*/
3856 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3857 {
3858   PetscFunctionBeginHot;
3859   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3860   if (numPoints) *numPoints = 0;
3861   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3862   PetscFunctionReturn(PETSC_SUCCESS);
3863 }
3864 
3865 /*@
3866   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3867 
3868   Not Collective
3869 
3870   Input Parameter:
3871 . mesh - The `DMPLEX`
3872 
3873   Output Parameters:
3874 + maxConeSize - The maximum number of in-edges
3875 - maxSupportSize - The maximum number of out-edges
3876 
3877   Level: beginner
3878 
3879 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3880 @*/
3881 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3882 {
3883   DM_Plex *mesh = (DM_Plex *)dm->data;
3884 
3885   PetscFunctionBegin;
3886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3887   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3888   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3889   PetscFunctionReturn(PETSC_SUCCESS);
3890 }
3891 
3892 PetscErrorCode DMSetUp_Plex(DM dm)
3893 {
3894   DM_Plex *mesh = (DM_Plex *)dm->data;
3895   PetscInt size, maxSupportSize;
3896 
3897   PetscFunctionBegin;
3898   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3899   PetscCall(PetscSectionSetUp(mesh->coneSection));
3900   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3901   PetscCall(PetscMalloc1(size, &mesh->cones));
3902   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3903   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3904   if (maxSupportSize) {
3905     PetscCall(PetscSectionSetUp(mesh->supportSection));
3906     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3907     PetscCall(PetscMalloc1(size, &mesh->supports));
3908   }
3909   PetscFunctionReturn(PETSC_SUCCESS);
3910 }
3911 
3912 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3913 {
3914   PetscFunctionBegin;
3915   if (subdm) PetscCall(DMClone(dm, subdm));
3916   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3917   if (subdm) (*subdm)->useNatural = dm->useNatural;
3918   if (dm->useNatural && dm->sfMigration) {
3919     PetscSF sfNatural;
3920 
3921     (*subdm)->sfMigration = dm->sfMigration;
3922     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3923     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3924     (*subdm)->sfNatural = sfNatural;
3925   }
3926   PetscFunctionReturn(PETSC_SUCCESS);
3927 }
3928 
3929 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3930 {
3931   PetscInt i = 0;
3932 
3933   PetscFunctionBegin;
3934   PetscCall(DMClone(dms[0], superdm));
3935   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3936   (*superdm)->useNatural = PETSC_FALSE;
3937   for (i = 0; i < len; i++) {
3938     if (dms[i]->useNatural && dms[i]->sfMigration) {
3939       PetscSF sfNatural;
3940 
3941       (*superdm)->sfMigration = dms[i]->sfMigration;
3942       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3943       (*superdm)->useNatural = PETSC_TRUE;
3944       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3945       (*superdm)->sfNatural = sfNatural;
3946       break;
3947     }
3948   }
3949   PetscFunctionReturn(PETSC_SUCCESS);
3950 }
3951 
3952 /*@
3953   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3954 
3955   Not Collective
3956 
3957   Input Parameter:
3958 . mesh - The `DMPLEX`
3959 
3960   Level: beginner
3961 
3962   Note:
3963   This should be called after all calls to `DMPlexSetCone()`
3964 
3965 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3966 @*/
3967 PetscErrorCode DMPlexSymmetrize(DM dm)
3968 {
3969   DM_Plex  *mesh = (DM_Plex *)dm->data;
3970   PetscInt *offsets;
3971   PetscInt  supportSize;
3972   PetscInt  pStart, pEnd, p;
3973 
3974   PetscFunctionBegin;
3975   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3976   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3977   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3978   /* Calculate support sizes */
3979   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3980   for (p = pStart; p < pEnd; ++p) {
3981     PetscInt dof, off, c;
3982 
3983     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3984     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3985     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3986   }
3987   PetscCall(PetscSectionSetUp(mesh->supportSection));
3988   /* Calculate supports */
3989   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3990   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3991   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3992   for (p = pStart; p < pEnd; ++p) {
3993     PetscInt dof, off, c;
3994 
3995     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3996     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3997     for (c = off; c < off + dof; ++c) {
3998       const PetscInt q = mesh->cones[c];
3999       PetscInt       offS;
4000 
4001       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4002 
4003       mesh->supports[offS + offsets[q]] = p;
4004       ++offsets[q];
4005     }
4006   }
4007   PetscCall(PetscFree(offsets));
4008   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4009   PetscFunctionReturn(PETSC_SUCCESS);
4010 }
4011 
4012 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4013 {
4014   IS stratumIS;
4015 
4016   PetscFunctionBegin;
4017   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4018   if (PetscDefined(USE_DEBUG)) {
4019     PetscInt  qStart, qEnd, numLevels, level;
4020     PetscBool overlap = PETSC_FALSE;
4021     PetscCall(DMLabelGetNumValues(label, &numLevels));
4022     for (level = 0; level < numLevels; level++) {
4023       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4024       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4025         overlap = PETSC_TRUE;
4026         break;
4027       }
4028     }
4029     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);
4030   }
4031   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4032   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4033   PetscCall(ISDestroy(&stratumIS));
4034   PetscFunctionReturn(PETSC_SUCCESS);
4035 }
4036 
4037 /*@
4038   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4039   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4040   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4041   the DAG.
4042 
4043   Collective
4044 
4045   Input Parameter:
4046 . mesh - The `DMPLEX`
4047 
4048   Level: beginner
4049 
4050   Notes:
4051   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4052   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4053   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4054   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4055   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4056 
4057   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4058   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4059   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
4060   to interpolate only that one (e0), so that
4061 .vb
4062   cone(c0) = {e0, v2}
4063   cone(e0) = {v0, v1}
4064 .ve
4065   If `DMPlexStratify()` is run on this mesh, it will give depths
4066 .vb
4067    depth 0 = {v0, v1, v2}
4068    depth 1 = {e0, c0}
4069 .ve
4070   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4071 
4072   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4073 
4074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4075 @*/
4076 PetscErrorCode DMPlexStratify(DM dm)
4077 {
4078   DM_Plex *mesh = (DM_Plex *)dm->data;
4079   DMLabel  label;
4080   PetscInt pStart, pEnd, p;
4081   PetscInt numRoots = 0, numLeaves = 0;
4082 
4083   PetscFunctionBegin;
4084   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4085   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4086 
4087   /* Create depth label */
4088   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4089   PetscCall(DMCreateLabel(dm, "depth"));
4090   PetscCall(DMPlexGetDepthLabel(dm, &label));
4091 
4092   {
4093     /* Initialize roots and count leaves */
4094     PetscInt sMin = PETSC_MAX_INT;
4095     PetscInt sMax = PETSC_MIN_INT;
4096     PetscInt coneSize, supportSize;
4097 
4098     for (p = pStart; p < pEnd; ++p) {
4099       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4100       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4101       if (!coneSize && supportSize) {
4102         sMin = PetscMin(p, sMin);
4103         sMax = PetscMax(p, sMax);
4104         ++numRoots;
4105       } else if (!supportSize && coneSize) {
4106         ++numLeaves;
4107       } else if (!supportSize && !coneSize) {
4108         /* Isolated points */
4109         sMin = PetscMin(p, sMin);
4110         sMax = PetscMax(p, sMax);
4111       }
4112     }
4113     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4114   }
4115 
4116   if (numRoots + numLeaves == (pEnd - pStart)) {
4117     PetscInt sMin = PETSC_MAX_INT;
4118     PetscInt sMax = PETSC_MIN_INT;
4119     PetscInt coneSize, supportSize;
4120 
4121     for (p = pStart; p < pEnd; ++p) {
4122       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4123       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4124       if (!supportSize && coneSize) {
4125         sMin = PetscMin(p, sMin);
4126         sMax = PetscMax(p, sMax);
4127       }
4128     }
4129     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4130   } else {
4131     PetscInt level = 0;
4132     PetscInt qStart, qEnd, q;
4133 
4134     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4135     while (qEnd > qStart) {
4136       PetscInt sMin = PETSC_MAX_INT;
4137       PetscInt sMax = PETSC_MIN_INT;
4138 
4139       for (q = qStart; q < qEnd; ++q) {
4140         const PetscInt *support;
4141         PetscInt        supportSize, s;
4142 
4143         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4144         PetscCall(DMPlexGetSupport(dm, q, &support));
4145         for (s = 0; s < supportSize; ++s) {
4146           sMin = PetscMin(support[s], sMin);
4147           sMax = PetscMax(support[s], sMax);
4148         }
4149       }
4150       PetscCall(DMLabelGetNumValues(label, &level));
4151       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4152       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4153     }
4154   }
4155   { /* just in case there is an empty process */
4156     PetscInt numValues, maxValues = 0, v;
4157 
4158     PetscCall(DMLabelGetNumValues(label, &numValues));
4159     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4160     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4161   }
4162   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4163   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4164   PetscFunctionReturn(PETSC_SUCCESS);
4165 }
4166 
4167 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4168 {
4169   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4170   PetscInt       dim, depth, pheight, coneSize;
4171 
4172   PetscFunctionBeginHot;
4173   PetscCall(DMGetDimension(dm, &dim));
4174   PetscCall(DMPlexGetDepth(dm, &depth));
4175   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4176   pheight = depth - pdepth;
4177   if (depth <= 1) {
4178     switch (pdepth) {
4179     case 0:
4180       ct = DM_POLYTOPE_POINT;
4181       break;
4182     case 1:
4183       switch (coneSize) {
4184       case 2:
4185         ct = DM_POLYTOPE_SEGMENT;
4186         break;
4187       case 3:
4188         ct = DM_POLYTOPE_TRIANGLE;
4189         break;
4190       case 4:
4191         switch (dim) {
4192         case 2:
4193           ct = DM_POLYTOPE_QUADRILATERAL;
4194           break;
4195         case 3:
4196           ct = DM_POLYTOPE_TETRAHEDRON;
4197           break;
4198         default:
4199           break;
4200         }
4201         break;
4202       case 5:
4203         ct = DM_POLYTOPE_PYRAMID;
4204         break;
4205       case 6:
4206         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4207         break;
4208       case 8:
4209         ct = DM_POLYTOPE_HEXAHEDRON;
4210         break;
4211       default:
4212         break;
4213       }
4214     }
4215   } else {
4216     if (pdepth == 0) {
4217       ct = DM_POLYTOPE_POINT;
4218     } else if (pheight == 0) {
4219       switch (dim) {
4220       case 1:
4221         switch (coneSize) {
4222         case 2:
4223           ct = DM_POLYTOPE_SEGMENT;
4224           break;
4225         default:
4226           break;
4227         }
4228         break;
4229       case 2:
4230         switch (coneSize) {
4231         case 3:
4232           ct = DM_POLYTOPE_TRIANGLE;
4233           break;
4234         case 4:
4235           ct = DM_POLYTOPE_QUADRILATERAL;
4236           break;
4237         default:
4238           break;
4239         }
4240         break;
4241       case 3:
4242         switch (coneSize) {
4243         case 4:
4244           ct = DM_POLYTOPE_TETRAHEDRON;
4245           break;
4246         case 5: {
4247           const PetscInt *cone;
4248           PetscInt        faceConeSize;
4249 
4250           PetscCall(DMPlexGetCone(dm, p, &cone));
4251           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4252           switch (faceConeSize) {
4253           case 3:
4254             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4255             break;
4256           case 4:
4257             ct = DM_POLYTOPE_PYRAMID;
4258             break;
4259           }
4260         } break;
4261         case 6:
4262           ct = DM_POLYTOPE_HEXAHEDRON;
4263           break;
4264         default:
4265           break;
4266         }
4267         break;
4268       default:
4269         break;
4270       }
4271     } else if (pheight > 0) {
4272       switch (coneSize) {
4273       case 2:
4274         ct = DM_POLYTOPE_SEGMENT;
4275         break;
4276       case 3:
4277         ct = DM_POLYTOPE_TRIANGLE;
4278         break;
4279       case 4:
4280         ct = DM_POLYTOPE_QUADRILATERAL;
4281         break;
4282       default:
4283         break;
4284       }
4285     }
4286   }
4287   *pt = ct;
4288   PetscFunctionReturn(PETSC_SUCCESS);
4289 }
4290 
4291 /*@
4292   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4293 
4294   Collective
4295 
4296   Input Parameter:
4297 . mesh - The `DMPLEX`
4298 
4299   Level: developer
4300 
4301   Note:
4302   This function is normally called automatically when a cell type is requested. It creates an
4303   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4304   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4305 
4306   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4307 
4308 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4309 @*/
4310 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4311 {
4312   DM_Plex *mesh;
4313   DMLabel  ctLabel;
4314   PetscInt pStart, pEnd, p;
4315 
4316   PetscFunctionBegin;
4317   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4318   mesh = (DM_Plex *)dm->data;
4319   PetscCall(DMCreateLabel(dm, "celltype"));
4320   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4321   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4322   for (p = pStart; p < pEnd; ++p) {
4323     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4324     PetscInt       pdepth;
4325 
4326     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4327     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4328     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4329     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4330   }
4331   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4332   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4333   PetscFunctionReturn(PETSC_SUCCESS);
4334 }
4335 
4336 /*@C
4337   DMPlexGetJoin - Get an array for the join of the set of points
4338 
4339   Not Collective
4340 
4341   Input Parameters:
4342 + dm - The `DMPLEX` object
4343 . numPoints - The number of input points for the join
4344 - points - The input points
4345 
4346   Output Parameters:
4347 + numCoveredPoints - The number of points in the join
4348 - coveredPoints - The points in the join
4349 
4350   Level: intermediate
4351 
4352   Note:
4353   Currently, this is restricted to a single level join
4354 
4355   Fortran Note:
4356   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4357 
4358 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4359 @*/
4360 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4361 {
4362   DM_Plex  *mesh = (DM_Plex *)dm->data;
4363   PetscInt *join[2];
4364   PetscInt  joinSize, i = 0;
4365   PetscInt  dof, off, p, c, m;
4366   PetscInt  maxSupportSize;
4367 
4368   PetscFunctionBegin;
4369   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4370   PetscValidIntPointer(points, 3);
4371   PetscValidIntPointer(numCoveredPoints, 4);
4372   PetscValidPointer(coveredPoints, 5);
4373   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4374   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4375   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4376   /* Copy in support of first point */
4377   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4378   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4379   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4380   /* Check each successive support */
4381   for (p = 1; p < numPoints; ++p) {
4382     PetscInt newJoinSize = 0;
4383 
4384     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4385     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4386     for (c = 0; c < dof; ++c) {
4387       const PetscInt point = mesh->supports[off + c];
4388 
4389       for (m = 0; m < joinSize; ++m) {
4390         if (point == join[i][m]) {
4391           join[1 - i][newJoinSize++] = point;
4392           break;
4393         }
4394       }
4395     }
4396     joinSize = newJoinSize;
4397     i        = 1 - i;
4398   }
4399   *numCoveredPoints = joinSize;
4400   *coveredPoints    = join[i];
4401   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4402   PetscFunctionReturn(PETSC_SUCCESS);
4403 }
4404 
4405 /*@C
4406   DMPlexRestoreJoin - Restore an array for the join of the set of points
4407 
4408   Not Collective
4409 
4410   Input Parameters:
4411 + dm - The `DMPLEX` object
4412 . numPoints - The number of input points for the join
4413 - points - The input points
4414 
4415   Output Parameters:
4416 + numCoveredPoints - The number of points in the join
4417 - coveredPoints - The points in the join
4418 
4419   Level: intermediate
4420 
4421   Fortran Note:
4422   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4423 
4424 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4425 @*/
4426 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4427 {
4428   PetscFunctionBegin;
4429   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4430   if (points) PetscValidIntPointer(points, 3);
4431   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4432   PetscValidPointer(coveredPoints, 5);
4433   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4434   if (numCoveredPoints) *numCoveredPoints = 0;
4435   PetscFunctionReturn(PETSC_SUCCESS);
4436 }
4437 
4438 /*@C
4439   DMPlexGetFullJoin - Get an array for the join of the set of points
4440 
4441   Not Collective
4442 
4443   Input Parameters:
4444 + dm - The `DMPLEX` object
4445 . numPoints - The number of input points for the join
4446 - points - The input points
4447 
4448   Output Parameters:
4449 + numCoveredPoints - The number of points in the join
4450 - coveredPoints - The points in the join
4451 
4452   Level: intermediate
4453 
4454   Fortran Note:
4455   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4456 
4457 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4458 @*/
4459 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4460 {
4461   PetscInt *offsets, **closures;
4462   PetscInt *join[2];
4463   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4464   PetscInt  p, d, c, m, ms;
4465 
4466   PetscFunctionBegin;
4467   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4468   PetscValidIntPointer(points, 3);
4469   PetscValidIntPointer(numCoveredPoints, 4);
4470   PetscValidPointer(coveredPoints, 5);
4471 
4472   PetscCall(DMPlexGetDepth(dm, &depth));
4473   PetscCall(PetscCalloc1(numPoints, &closures));
4474   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4475   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4476   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4477   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4478   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4479 
4480   for (p = 0; p < numPoints; ++p) {
4481     PetscInt closureSize;
4482 
4483     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4484 
4485     offsets[p * (depth + 2) + 0] = 0;
4486     for (d = 0; d < depth + 1; ++d) {
4487       PetscInt pStart, pEnd, i;
4488 
4489       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4490       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4491         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4492           offsets[p * (depth + 2) + d + 1] = i;
4493           break;
4494         }
4495       }
4496       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4497     }
4498     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);
4499   }
4500   for (d = 0; d < depth + 1; ++d) {
4501     PetscInt dof;
4502 
4503     /* Copy in support of first point */
4504     dof = offsets[d + 1] - offsets[d];
4505     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4506     /* Check each successive cone */
4507     for (p = 1; p < numPoints && joinSize; ++p) {
4508       PetscInt newJoinSize = 0;
4509 
4510       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4511       for (c = 0; c < dof; ++c) {
4512         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4513 
4514         for (m = 0; m < joinSize; ++m) {
4515           if (point == join[i][m]) {
4516             join[1 - i][newJoinSize++] = point;
4517             break;
4518           }
4519         }
4520       }
4521       joinSize = newJoinSize;
4522       i        = 1 - i;
4523     }
4524     if (joinSize) break;
4525   }
4526   *numCoveredPoints = joinSize;
4527   *coveredPoints    = join[i];
4528   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4529   PetscCall(PetscFree(closures));
4530   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4531   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4532   PetscFunctionReturn(PETSC_SUCCESS);
4533 }
4534 
4535 /*@C
4536   DMPlexGetMeet - Get an array for the meet of the set of points
4537 
4538   Not Collective
4539 
4540   Input Parameters:
4541 + dm - The `DMPLEX` object
4542 . numPoints - The number of input points for the meet
4543 - points - The input points
4544 
4545   Output Parameters:
4546 + numCoveredPoints - The number of points in the meet
4547 - coveredPoints - The points in the meet
4548 
4549   Level: intermediate
4550 
4551   Note:
4552   Currently, this is restricted to a single level meet
4553 
4554   Fortran Notes:
4555   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4556 
4557 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4558 @*/
4559 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4560 {
4561   DM_Plex  *mesh = (DM_Plex *)dm->data;
4562   PetscInt *meet[2];
4563   PetscInt  meetSize, i = 0;
4564   PetscInt  dof, off, p, c, m;
4565   PetscInt  maxConeSize;
4566 
4567   PetscFunctionBegin;
4568   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4569   PetscValidIntPointer(points, 3);
4570   PetscValidIntPointer(numCoveringPoints, 4);
4571   PetscValidPointer(coveringPoints, 5);
4572   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4573   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4574   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4575   /* Copy in cone of first point */
4576   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4577   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4578   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4579   /* Check each successive cone */
4580   for (p = 1; p < numPoints; ++p) {
4581     PetscInt newMeetSize = 0;
4582 
4583     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4584     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4585     for (c = 0; c < dof; ++c) {
4586       const PetscInt point = mesh->cones[off + c];
4587 
4588       for (m = 0; m < meetSize; ++m) {
4589         if (point == meet[i][m]) {
4590           meet[1 - i][newMeetSize++] = point;
4591           break;
4592         }
4593       }
4594     }
4595     meetSize = newMeetSize;
4596     i        = 1 - i;
4597   }
4598   *numCoveringPoints = meetSize;
4599   *coveringPoints    = meet[i];
4600   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4601   PetscFunctionReturn(PETSC_SUCCESS);
4602 }
4603 
4604 /*@C
4605   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4606 
4607   Not Collective
4608 
4609   Input Parameters:
4610 + dm - The `DMPLEX` object
4611 . numPoints - The number of input points for the meet
4612 - points - The input points
4613 
4614   Output Parameters:
4615 + numCoveredPoints - The number of points in the meet
4616 - coveredPoints - The points in the meet
4617 
4618   Level: intermediate
4619 
4620   Fortran Note:
4621   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4622 
4623 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4624 @*/
4625 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4626 {
4627   PetscFunctionBegin;
4628   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4629   if (points) PetscValidIntPointer(points, 3);
4630   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4631   PetscValidPointer(coveredPoints, 5);
4632   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4633   if (numCoveredPoints) *numCoveredPoints = 0;
4634   PetscFunctionReturn(PETSC_SUCCESS);
4635 }
4636 
4637 /*@C
4638   DMPlexGetFullMeet - Get an array for the meet of the set of points
4639 
4640   Not Collective
4641 
4642   Input Parameters:
4643 + dm - The `DMPLEX` object
4644 . numPoints - The number of input points for the meet
4645 - points - The input points
4646 
4647   Output Parameters:
4648 + numCoveredPoints - The number of points in the meet
4649 - coveredPoints - The points in the meet
4650 
4651   Level: intermediate
4652 
4653   Fortran Note:
4654   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4655 
4656 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4657 @*/
4658 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4659 {
4660   PetscInt *offsets, **closures;
4661   PetscInt *meet[2];
4662   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4663   PetscInt  p, h, c, m, mc;
4664 
4665   PetscFunctionBegin;
4666   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4667   PetscValidIntPointer(points, 3);
4668   PetscValidIntPointer(numCoveredPoints, 4);
4669   PetscValidPointer(coveredPoints, 5);
4670 
4671   PetscCall(DMPlexGetDepth(dm, &height));
4672   PetscCall(PetscMalloc1(numPoints, &closures));
4673   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4674   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4675   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4676   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4677   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4678 
4679   for (p = 0; p < numPoints; ++p) {
4680     PetscInt closureSize;
4681 
4682     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4683 
4684     offsets[p * (height + 2) + 0] = 0;
4685     for (h = 0; h < height + 1; ++h) {
4686       PetscInt pStart, pEnd, i;
4687 
4688       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4689       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4690         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4691           offsets[p * (height + 2) + h + 1] = i;
4692           break;
4693         }
4694       }
4695       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4696     }
4697     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);
4698   }
4699   for (h = 0; h < height + 1; ++h) {
4700     PetscInt dof;
4701 
4702     /* Copy in cone of first point */
4703     dof = offsets[h + 1] - offsets[h];
4704     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4705     /* Check each successive cone */
4706     for (p = 1; p < numPoints && meetSize; ++p) {
4707       PetscInt newMeetSize = 0;
4708 
4709       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4710       for (c = 0; c < dof; ++c) {
4711         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4712 
4713         for (m = 0; m < meetSize; ++m) {
4714           if (point == meet[i][m]) {
4715             meet[1 - i][newMeetSize++] = point;
4716             break;
4717           }
4718         }
4719       }
4720       meetSize = newMeetSize;
4721       i        = 1 - i;
4722     }
4723     if (meetSize) break;
4724   }
4725   *numCoveredPoints = meetSize;
4726   *coveredPoints    = meet[i];
4727   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4728   PetscCall(PetscFree(closures));
4729   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4730   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4731   PetscFunctionReturn(PETSC_SUCCESS);
4732 }
4733 
4734 /*@C
4735   DMPlexEqual - Determine if two `DM` have the same topology
4736 
4737   Not Collective
4738 
4739   Input Parameters:
4740 + dmA - A `DMPLEX` object
4741 - dmB - A `DMPLEX` object
4742 
4743   Output Parameter:
4744 . equal - `PETSC_TRUE` if the topologies are identical
4745 
4746   Level: intermediate
4747 
4748   Note:
4749   We are not solving graph isomorphism, so we do not permute.
4750 
4751 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4752 @*/
4753 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4754 {
4755   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4756 
4757   PetscFunctionBegin;
4758   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4759   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4760   PetscValidBoolPointer(equal, 3);
4761 
4762   *equal = PETSC_FALSE;
4763   PetscCall(DMPlexGetDepth(dmA, &depth));
4764   PetscCall(DMPlexGetDepth(dmB, &depthB));
4765   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4766   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4767   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4768   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4769   for (p = pStart; p < pEnd; ++p) {
4770     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4771     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4772 
4773     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4774     PetscCall(DMPlexGetCone(dmA, p, &cone));
4775     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4776     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4777     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4778     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4779     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4780     for (c = 0; c < coneSize; ++c) {
4781       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4782       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4783     }
4784     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4785     PetscCall(DMPlexGetSupport(dmA, p, &support));
4786     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4787     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4788     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4789     for (s = 0; s < supportSize; ++s) {
4790       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4791     }
4792   }
4793   *equal = PETSC_TRUE;
4794   PetscFunctionReturn(PETSC_SUCCESS);
4795 }
4796 
4797 /*@C
4798   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4799 
4800   Not Collective
4801 
4802   Input Parameters:
4803 + dm         - The `DMPLEX`
4804 . cellDim    - The cell dimension
4805 - numCorners - The number of vertices on a cell
4806 
4807   Output Parameter:
4808 . numFaceVertices - The number of vertices on a face
4809 
4810   Level: developer
4811 
4812   Note:
4813   Of course this can only work for a restricted set of symmetric shapes
4814 
4815 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4816 @*/
4817 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4818 {
4819   MPI_Comm comm;
4820 
4821   PetscFunctionBegin;
4822   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4823   PetscValidIntPointer(numFaceVertices, 4);
4824   switch (cellDim) {
4825   case 0:
4826     *numFaceVertices = 0;
4827     break;
4828   case 1:
4829     *numFaceVertices = 1;
4830     break;
4831   case 2:
4832     switch (numCorners) {
4833     case 3:                 /* triangle */
4834       *numFaceVertices = 2; /* Edge has 2 vertices */
4835       break;
4836     case 4:                 /* quadrilateral */
4837       *numFaceVertices = 2; /* Edge has 2 vertices */
4838       break;
4839     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4840       *numFaceVertices = 3; /* Edge has 3 vertices */
4841       break;
4842     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4843       *numFaceVertices = 3; /* Edge has 3 vertices */
4844       break;
4845     default:
4846       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4847     }
4848     break;
4849   case 3:
4850     switch (numCorners) {
4851     case 4:                 /* tetradehdron */
4852       *numFaceVertices = 3; /* Face has 3 vertices */
4853       break;
4854     case 6:                 /* tet cohesive cells */
4855       *numFaceVertices = 4; /* Face has 4 vertices */
4856       break;
4857     case 8:                 /* hexahedron */
4858       *numFaceVertices = 4; /* Face has 4 vertices */
4859       break;
4860     case 9:                 /* tet cohesive Lagrange cells */
4861       *numFaceVertices = 6; /* Face has 6 vertices */
4862       break;
4863     case 10:                /* quadratic tetrahedron */
4864       *numFaceVertices = 6; /* Face has 6 vertices */
4865       break;
4866     case 12:                /* hex cohesive Lagrange cells */
4867       *numFaceVertices = 6; /* Face has 6 vertices */
4868       break;
4869     case 18:                /* quadratic tet cohesive Lagrange cells */
4870       *numFaceVertices = 6; /* Face has 6 vertices */
4871       break;
4872     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4873       *numFaceVertices = 9; /* Face has 9 vertices */
4874       break;
4875     default:
4876       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4877     }
4878     break;
4879   default:
4880     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4881   }
4882   PetscFunctionReturn(PETSC_SUCCESS);
4883 }
4884 
4885 /*@
4886   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4887 
4888   Not Collective
4889 
4890   Input Parameter:
4891 . dm    - The `DMPLEX` object
4892 
4893   Output Parameter:
4894 . depthLabel - The `DMLabel` recording point depth
4895 
4896   Level: developer
4897 
4898 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4899 @*/
4900 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4901 {
4902   PetscFunctionBegin;
4903   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4904   PetscValidPointer(depthLabel, 2);
4905   *depthLabel = dm->depthLabel;
4906   PetscFunctionReturn(PETSC_SUCCESS);
4907 }
4908 
4909 /*@
4910   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4911 
4912   Not Collective
4913 
4914   Input Parameter:
4915 . dm    - The `DMPLEX` object
4916 
4917   Output Parameter:
4918 . depth - The number of strata (breadth first levels) in the DAG
4919 
4920   Level: developer
4921 
4922   Notes:
4923   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4924 
4925   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4926 
4927   An empty mesh gives -1.
4928 
4929 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4930 @*/
4931 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4932 {
4933   DM_Plex *mesh = (DM_Plex *)dm->data;
4934   DMLabel  label;
4935   PetscInt d = 0;
4936 
4937   PetscFunctionBegin;
4938   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4939   PetscValidIntPointer(depth, 2);
4940   if (mesh->tr) {
4941     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4942   } else {
4943     PetscCall(DMPlexGetDepthLabel(dm, &label));
4944     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4945     *depth = d - 1;
4946   }
4947   PetscFunctionReturn(PETSC_SUCCESS);
4948 }
4949 
4950 /*@
4951   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
4952 
4953   Not Collective
4954 
4955   Input Parameters:
4956 + dm    - The `DMPLEX` object
4957 - depth - The requested depth
4958 
4959   Output Parameters:
4960 + start - The first point at this `depth`
4961 - end   - One beyond the last point at this `depth`
4962 
4963   Level: developer
4964 
4965   Notes:
4966   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4967   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
4968   higher dimension, e.g., "edges".
4969 
4970 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4971 @*/
4972 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4973 {
4974   DM_Plex *mesh = (DM_Plex *)dm->data;
4975   DMLabel  label;
4976   PetscInt pStart, pEnd;
4977 
4978   PetscFunctionBegin;
4979   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4980   if (start) {
4981     PetscValidIntPointer(start, 3);
4982     *start = 0;
4983   }
4984   if (end) {
4985     PetscValidIntPointer(end, 4);
4986     *end = 0;
4987   }
4988   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4989   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4990   if (depth < 0) {
4991     if (start) *start = pStart;
4992     if (end) *end = pEnd;
4993     PetscFunctionReturn(PETSC_SUCCESS);
4994   }
4995   if (mesh->tr) {
4996     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
4997   } else {
4998     PetscCall(DMPlexGetDepthLabel(dm, &label));
4999     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5000     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5001   }
5002   PetscFunctionReturn(PETSC_SUCCESS);
5003 }
5004 
5005 /*@
5006   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5007 
5008   Not Collective
5009 
5010   Input Parameters:
5011 + dm     - The `DMPLEX` object
5012 - height - The requested height
5013 
5014   Output Parameters:
5015 + start - The first point at this `height`
5016 - end   - One beyond the last point at this `height`
5017 
5018   Level: developer
5019 
5020   Notes:
5021   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5022   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5023   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5024 
5025 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5026 @*/
5027 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5028 {
5029   DMLabel  label;
5030   PetscInt depth, pStart, pEnd;
5031 
5032   PetscFunctionBegin;
5033   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5034   if (start) {
5035     PetscValidIntPointer(start, 3);
5036     *start = 0;
5037   }
5038   if (end) {
5039     PetscValidIntPointer(end, 4);
5040     *end = 0;
5041   }
5042   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5043   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5044   if (height < 0) {
5045     if (start) *start = pStart;
5046     if (end) *end = pEnd;
5047     PetscFunctionReturn(PETSC_SUCCESS);
5048   }
5049   PetscCall(DMPlexGetDepthLabel(dm, &label));
5050   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5051   PetscCall(DMLabelGetNumValues(label, &depth));
5052   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5053   PetscFunctionReturn(PETSC_SUCCESS);
5054 }
5055 
5056 /*@
5057   DMPlexGetPointDepth - Get the `depth` of a given point
5058 
5059   Not Collective
5060 
5061   Input Parameters:
5062 + dm    - The `DMPLEX` object
5063 - point - The point
5064 
5065   Output Parameter:
5066 . depth - The depth of the `point`
5067 
5068   Level: intermediate
5069 
5070 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5071 @*/
5072 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5073 {
5074   PetscFunctionBegin;
5075   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5076   PetscValidIntPointer(depth, 3);
5077   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5078   PetscFunctionReturn(PETSC_SUCCESS);
5079 }
5080 
5081 /*@
5082   DMPlexGetPointHeight - Get the `height` of a given point
5083 
5084   Not Collective
5085 
5086   Input Parameters:
5087 + dm    - The `DMPLEX` object
5088 - point - The point
5089 
5090   Output Parameter:
5091 . height - The height of the `point`
5092 
5093   Level: intermediate
5094 
5095 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5096 @*/
5097 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5098 {
5099   PetscInt n, pDepth;
5100 
5101   PetscFunctionBegin;
5102   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5103   PetscValidIntPointer(height, 3);
5104   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5105   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5106   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5107   PetscFunctionReturn(PETSC_SUCCESS);
5108 }
5109 
5110 /*@
5111   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5112 
5113   Not Collective
5114 
5115   Input Parameter:
5116 . dm - The `DMPLEX` object
5117 
5118   Output Parameter:
5119 . celltypeLabel - The `DMLabel` recording cell polytope type
5120 
5121   Level: developer
5122 
5123   Note:
5124   This function will trigger automatica computation of cell types. This can be disabled by calling
5125   `DMCreateLabel`(dm, "celltype") beforehand.
5126 
5127 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5128 @*/
5129 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5130 {
5131   PetscFunctionBegin;
5132   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5133   PetscValidPointer(celltypeLabel, 2);
5134   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5135   *celltypeLabel = dm->celltypeLabel;
5136   PetscFunctionReturn(PETSC_SUCCESS);
5137 }
5138 
5139 /*@
5140   DMPlexGetCellType - Get the polytope type of a given cell
5141 
5142   Not Collective
5143 
5144   Input Parameters:
5145 + dm   - The `DMPLEX` object
5146 - cell - The cell
5147 
5148   Output Parameter:
5149 . celltype - The polytope type of the cell
5150 
5151   Level: intermediate
5152 
5153 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5154 @*/
5155 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5156 {
5157   DM_Plex *mesh = (DM_Plex *)dm->data;
5158   DMLabel  label;
5159   PetscInt ct;
5160 
5161   PetscFunctionBegin;
5162   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5163   PetscValidPointer(celltype, 3);
5164   if (mesh->tr) {
5165     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5166   } else {
5167     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5168     PetscCall(DMLabelGetValue(label, cell, &ct));
5169     PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5170     *celltype = (DMPolytopeType)ct;
5171   }
5172   PetscFunctionReturn(PETSC_SUCCESS);
5173 }
5174 
5175 /*@
5176   DMPlexSetCellType - Set the polytope type of a given cell
5177 
5178   Not Collective
5179 
5180   Input Parameters:
5181 + dm   - The `DMPLEX` object
5182 . cell - The cell
5183 - celltype - The polytope type of the cell
5184 
5185   Level: advanced
5186 
5187   Note:
5188   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5189   is executed. This function will override the computed type. However, if automatic classification will not succeed
5190   and a user wants to manually specify all types, the classification must be disabled by calling
5191   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5192 
5193 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5194 @*/
5195 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5196 {
5197   DMLabel label;
5198 
5199   PetscFunctionBegin;
5200   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5201   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5202   PetscCall(DMLabelSetValue(label, cell, celltype));
5203   PetscFunctionReturn(PETSC_SUCCESS);
5204 }
5205 
5206 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5207 {
5208   PetscSection section, s;
5209   Mat          m;
5210   PetscInt     maxHeight;
5211   const char  *prefix;
5212 
5213   PetscFunctionBegin;
5214   PetscCall(DMClone(dm, cdm));
5215   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5216   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5217   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5218   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5219   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5220   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5221   PetscCall(DMSetLocalSection(*cdm, section));
5222   PetscCall(PetscSectionDestroy(&section));
5223   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5224   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5225   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5226   PetscCall(PetscSectionDestroy(&s));
5227   PetscCall(MatDestroy(&m));
5228 
5229   PetscCall(DMSetNumFields(*cdm, 1));
5230   PetscCall(DMCreateDS(*cdm));
5231   (*cdm)->cloneOpts = PETSC_TRUE;
5232   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5233   PetscFunctionReturn(PETSC_SUCCESS);
5234 }
5235 
5236 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5237 {
5238   Vec coordsLocal, cellCoordsLocal;
5239   DM  coordsDM, cellCoordsDM;
5240 
5241   PetscFunctionBegin;
5242   *field = NULL;
5243   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5244   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5245   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5246   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5247   if (coordsLocal && coordsDM) {
5248     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5249     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5250   }
5251   PetscFunctionReturn(PETSC_SUCCESS);
5252 }
5253 
5254 /*@C
5255   DMPlexGetConeSection - Return a section which describes the layout of cone data
5256 
5257   Not Collective
5258 
5259   Input Parameter:
5260 . dm        - The `DMPLEX` object
5261 
5262   Output Parameter:
5263 . section - The `PetscSection` object
5264 
5265   Level: developer
5266 
5267 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5268 @*/
5269 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5270 {
5271   DM_Plex *mesh = (DM_Plex *)dm->data;
5272 
5273   PetscFunctionBegin;
5274   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5275   if (section) *section = mesh->coneSection;
5276   PetscFunctionReturn(PETSC_SUCCESS);
5277 }
5278 
5279 /*@C
5280   DMPlexGetSupportSection - Return a section which describes the layout of support data
5281 
5282   Not Collective
5283 
5284   Input Parameter:
5285 . dm        - The `DMPLEX` object
5286 
5287   Output Parameter:
5288 . section - The `PetscSection` object
5289 
5290   Level: developer
5291 
5292 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5293 @*/
5294 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5295 {
5296   DM_Plex *mesh = (DM_Plex *)dm->data;
5297 
5298   PetscFunctionBegin;
5299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5300   if (section) *section = mesh->supportSection;
5301   PetscFunctionReturn(PETSC_SUCCESS);
5302 }
5303 
5304 /*@C
5305   DMPlexGetCones - Return cone data
5306 
5307   Not Collective
5308 
5309   Input Parameter:
5310 . dm        - The `DMPLEX` object
5311 
5312   Output Parameter:
5313 . cones - The cone for each point
5314 
5315   Level: developer
5316 
5317 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5318 @*/
5319 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5320 {
5321   DM_Plex *mesh = (DM_Plex *)dm->data;
5322 
5323   PetscFunctionBegin;
5324   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5325   if (cones) *cones = mesh->cones;
5326   PetscFunctionReturn(PETSC_SUCCESS);
5327 }
5328 
5329 /*@C
5330   DMPlexGetConeOrientations - Return cone orientation data
5331 
5332   Not Collective
5333 
5334   Input Parameter:
5335 . dm        - The `DMPLEX` object
5336 
5337   Output Parameter:
5338 . coneOrientations - The array of cone orientations for all points
5339 
5340   Level: developer
5341 
5342   Notes:
5343   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5344 
5345   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5346 
5347 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5348 @*/
5349 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5350 {
5351   DM_Plex *mesh = (DM_Plex *)dm->data;
5352 
5353   PetscFunctionBegin;
5354   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5355   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5356   PetscFunctionReturn(PETSC_SUCCESS);
5357 }
5358 
5359 /******************************** FEM Support **********************************/
5360 
5361 /*
5362  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5363  representing a line in the section.
5364 */
5365 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5366 {
5367   PetscFunctionBeginHot;
5368   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5369   if (line < 0) {
5370     *k  = 0;
5371     *Nc = 0;
5372   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5373     *k = 1;
5374   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5375     /* An order k SEM disc has k-1 dofs on an edge */
5376     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5377     *k = *k / *Nc + 1;
5378   }
5379   PetscFunctionReturn(PETSC_SUCCESS);
5380 }
5381 
5382 /*@
5383 
5384   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5385   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5386   section provided (or the section of the `DM`).
5387 
5388   Input Parameters:
5389 + dm      - The `DM`
5390 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5391 - section - The `PetscSection` to reorder, or `NULL` for the default section
5392 
5393   Example:
5394   A typical interpolated single-quad mesh might order points as
5395 .vb
5396   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5397 
5398   v4 -- e6 -- v3
5399   |           |
5400   e7    c0    e8
5401   |           |
5402   v1 -- e5 -- v2
5403 .ve
5404 
5405   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5406   dofs in the order of points, e.g.,
5407 .vb
5408     c0 -> [0,1,2,3]
5409     v1 -> [4]
5410     ...
5411     e5 -> [8, 9]
5412 .ve
5413 
5414   which corresponds to the dofs
5415 .vb
5416     6   10  11  7
5417     13  2   3   15
5418     12  0   1   14
5419     4   8   9   5
5420 .ve
5421 
5422   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5423 .vb
5424   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5425 .ve
5426 
5427   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5428 .vb
5429    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5430 .ve
5431 
5432   Level: developer
5433 
5434   Note:
5435   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5436   degree of the basis.
5437 
5438 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5439 @*/
5440 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5441 {
5442   DMLabel   label;
5443   PetscInt  dim, depth = -1, eStart = -1, Nf;
5444   PetscBool vertexchart;
5445 
5446   PetscFunctionBegin;
5447   PetscCall(DMGetDimension(dm, &dim));
5448   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5449   if (point < 0) {
5450     PetscInt sStart, sEnd;
5451 
5452     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5453     point = sEnd - sStart ? sStart : point;
5454   }
5455   PetscCall(DMPlexGetDepthLabel(dm, &label));
5456   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5457   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5458   if (depth == 1) {
5459     eStart = point;
5460   } else if (depth == dim) {
5461     const PetscInt *cone;
5462 
5463     PetscCall(DMPlexGetCone(dm, point, &cone));
5464     if (dim == 2) eStart = cone[0];
5465     else if (dim == 3) {
5466       const PetscInt *cone2;
5467       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5468       eStart = cone2[0];
5469     } 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);
5470   } 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);
5471   { /* Determine whether the chart covers all points or just vertices. */
5472     PetscInt pStart, pEnd, cStart, cEnd;
5473     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5474     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5475     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5476     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5477     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5478   }
5479   PetscCall(PetscSectionGetNumFields(section, &Nf));
5480   for (PetscInt d = 1; d <= dim; d++) {
5481     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5482     PetscInt *perm;
5483 
5484     for (f = 0; f < Nf; ++f) {
5485       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5486       size += PetscPowInt(k + 1, d) * Nc;
5487     }
5488     PetscCall(PetscMalloc1(size, &perm));
5489     for (f = 0; f < Nf; ++f) {
5490       switch (d) {
5491       case 1:
5492         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5493         /*
5494          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5495          We want              [ vtx0; edge of length k-1; vtx1 ]
5496          */
5497         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5498         for (i = 0; i < k - 1; i++)
5499           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5500         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5501         foffset = offset;
5502         break;
5503       case 2:
5504         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5505         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5506         /* The SEM order is
5507 
5508          v_lb, {e_b}, v_rb,
5509          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5510          v_lt, reverse {e_t}, v_rt
5511          */
5512         {
5513           const PetscInt of   = 0;
5514           const PetscInt oeb  = of + PetscSqr(k - 1);
5515           const PetscInt oer  = oeb + (k - 1);
5516           const PetscInt oet  = oer + (k - 1);
5517           const PetscInt oel  = oet + (k - 1);
5518           const PetscInt ovlb = oel + (k - 1);
5519           const PetscInt ovrb = ovlb + 1;
5520           const PetscInt ovrt = ovrb + 1;
5521           const PetscInt ovlt = ovrt + 1;
5522           PetscInt       o;
5523 
5524           /* bottom */
5525           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5526           for (o = oeb; o < oer; ++o)
5527             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5528           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5529           /* middle */
5530           for (i = 0; i < k - 1; ++i) {
5531             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5532             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5533               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5534             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5535           }
5536           /* top */
5537           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5538           for (o = oel - 1; o >= oet; --o)
5539             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5540           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5541           foffset = offset;
5542         }
5543         break;
5544       case 3:
5545         /* The original hex closure is
5546 
5547          {c,
5548          f_b, f_t, f_f, f_b, f_r, f_l,
5549          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5550          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5551          */
5552         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5553         /* The SEM order is
5554          Bottom Slice
5555          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5556          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5557          v_blb, {e_bb}, v_brb,
5558 
5559          Middle Slice (j)
5560          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5561          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5562          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5563 
5564          Top Slice
5565          v_tlf, {e_tf}, v_trf,
5566          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5567          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5568          */
5569         {
5570           const PetscInt oc    = 0;
5571           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5572           const PetscInt oft   = ofb + PetscSqr(k - 1);
5573           const PetscInt off   = oft + PetscSqr(k - 1);
5574           const PetscInt ofk   = off + PetscSqr(k - 1);
5575           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5576           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5577           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5578           const PetscInt oebb  = oebl + (k - 1);
5579           const PetscInt oebr  = oebb + (k - 1);
5580           const PetscInt oebf  = oebr + (k - 1);
5581           const PetscInt oetf  = oebf + (k - 1);
5582           const PetscInt oetr  = oetf + (k - 1);
5583           const PetscInt oetb  = oetr + (k - 1);
5584           const PetscInt oetl  = oetb + (k - 1);
5585           const PetscInt oerf  = oetl + (k - 1);
5586           const PetscInt oelf  = oerf + (k - 1);
5587           const PetscInt oelb  = oelf + (k - 1);
5588           const PetscInt oerb  = oelb + (k - 1);
5589           const PetscInt ovblf = oerb + (k - 1);
5590           const PetscInt ovblb = ovblf + 1;
5591           const PetscInt ovbrb = ovblb + 1;
5592           const PetscInt ovbrf = ovbrb + 1;
5593           const PetscInt ovtlf = ovbrf + 1;
5594           const PetscInt ovtrf = ovtlf + 1;
5595           const PetscInt ovtrb = ovtrf + 1;
5596           const PetscInt ovtlb = ovtrb + 1;
5597           PetscInt       o, n;
5598 
5599           /* Bottom Slice */
5600           /*   bottom */
5601           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5602           for (o = oetf - 1; o >= oebf; --o)
5603             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5604           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5605           /*   middle */
5606           for (i = 0; i < k - 1; ++i) {
5607             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5608             for (n = 0; n < k - 1; ++n) {
5609               o = ofb + n * (k - 1) + i;
5610               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5611             }
5612             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5613           }
5614           /*   top */
5615           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5616           for (o = oebb; o < oebr; ++o)
5617             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5618           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5619 
5620           /* Middle Slice */
5621           for (j = 0; j < k - 1; ++j) {
5622             /*   bottom */
5623             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5624             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5625               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5626             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5627             /*   middle */
5628             for (i = 0; i < k - 1; ++i) {
5629               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5630               for (n = 0; n < k - 1; ++n)
5631                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5632               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5633             }
5634             /*   top */
5635             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5636             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5637               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5638             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5639           }
5640 
5641           /* Top Slice */
5642           /*   bottom */
5643           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5644           for (o = oetf; o < oetr; ++o)
5645             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5646           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5647           /*   middle */
5648           for (i = 0; i < k - 1; ++i) {
5649             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5650             for (n = 0; n < k - 1; ++n)
5651               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5652             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5653           }
5654           /*   top */
5655           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5656           for (o = oetl - 1; o >= oetb; --o)
5657             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5658           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5659 
5660           foffset = offset;
5661         }
5662         break;
5663       default:
5664         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5665       }
5666     }
5667     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5668     /* Check permutation */
5669     {
5670       PetscInt *check;
5671 
5672       PetscCall(PetscMalloc1(size, &check));
5673       for (i = 0; i < size; ++i) {
5674         check[i] = -1;
5675         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5676       }
5677       for (i = 0; i < size; ++i) check[perm[i]] = i;
5678       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5679       PetscCall(PetscFree(check));
5680     }
5681     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5682     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5683       PetscInt *loc_perm;
5684       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5685       for (PetscInt i = 0; i < size; i++) {
5686         loc_perm[i]        = perm[i];
5687         loc_perm[size + i] = size + perm[i];
5688       }
5689       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5690     }
5691   }
5692   PetscFunctionReturn(PETSC_SUCCESS);
5693 }
5694 
5695 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5696 {
5697   PetscDS  prob;
5698   PetscInt depth, Nf, h;
5699   DMLabel  label;
5700 
5701   PetscFunctionBeginHot;
5702   PetscCall(DMGetDS(dm, &prob));
5703   Nf      = prob->Nf;
5704   label   = dm->depthLabel;
5705   *dspace = NULL;
5706   if (field < Nf) {
5707     PetscObject disc = prob->disc[field];
5708 
5709     if (disc->classid == PETSCFE_CLASSID) {
5710       PetscDualSpace dsp;
5711 
5712       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5713       PetscCall(DMLabelGetNumValues(label, &depth));
5714       PetscCall(DMLabelGetValue(label, point, &h));
5715       h = depth - 1 - h;
5716       if (h) {
5717         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5718       } else {
5719         *dspace = dsp;
5720       }
5721     }
5722   }
5723   PetscFunctionReturn(PETSC_SUCCESS);
5724 }
5725 
5726 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5727 {
5728   PetscScalar       *array;
5729   const PetscScalar *vArray;
5730   const PetscInt    *cone, *coneO;
5731   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5732 
5733   PetscFunctionBeginHot;
5734   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5735   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5736   PetscCall(DMPlexGetCone(dm, point, &cone));
5737   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5738   if (!values || !*values) {
5739     if ((point >= pStart) && (point < pEnd)) {
5740       PetscInt dof;
5741 
5742       PetscCall(PetscSectionGetDof(section, point, &dof));
5743       size += dof;
5744     }
5745     for (p = 0; p < numPoints; ++p) {
5746       const PetscInt cp = cone[p];
5747       PetscInt       dof;
5748 
5749       if ((cp < pStart) || (cp >= pEnd)) continue;
5750       PetscCall(PetscSectionGetDof(section, cp, &dof));
5751       size += dof;
5752     }
5753     if (!values) {
5754       if (csize) *csize = size;
5755       PetscFunctionReturn(PETSC_SUCCESS);
5756     }
5757     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5758   } else {
5759     array = *values;
5760   }
5761   size = 0;
5762   PetscCall(VecGetArrayRead(v, &vArray));
5763   if ((point >= pStart) && (point < pEnd)) {
5764     PetscInt           dof, off, d;
5765     const PetscScalar *varr;
5766 
5767     PetscCall(PetscSectionGetDof(section, point, &dof));
5768     PetscCall(PetscSectionGetOffset(section, point, &off));
5769     varr = &vArray[off];
5770     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5771     size += dof;
5772   }
5773   for (p = 0; p < numPoints; ++p) {
5774     const PetscInt     cp = cone[p];
5775     PetscInt           o  = coneO[p];
5776     PetscInt           dof, off, d;
5777     const PetscScalar *varr;
5778 
5779     if ((cp < pStart) || (cp >= pEnd)) continue;
5780     PetscCall(PetscSectionGetDof(section, cp, &dof));
5781     PetscCall(PetscSectionGetOffset(section, cp, &off));
5782     varr = &vArray[off];
5783     if (o >= 0) {
5784       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5785     } else {
5786       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5787     }
5788     size += dof;
5789   }
5790   PetscCall(VecRestoreArrayRead(v, &vArray));
5791   if (!*values) {
5792     if (csize) *csize = size;
5793     *values = array;
5794   } else {
5795     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5796     *csize = size;
5797   }
5798   PetscFunctionReturn(PETSC_SUCCESS);
5799 }
5800 
5801 /* Compress out points not in the section */
5802 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5803 {
5804   const PetscInt np = *numPoints;
5805   PetscInt       pStart, pEnd, p, q;
5806 
5807   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5808   for (p = 0, q = 0; p < np; ++p) {
5809     const PetscInt r = points[p * 2];
5810     if ((r >= pStart) && (r < pEnd)) {
5811       points[q * 2]     = r;
5812       points[q * 2 + 1] = points[p * 2 + 1];
5813       ++q;
5814     }
5815   }
5816   *numPoints = q;
5817   return PETSC_SUCCESS;
5818 }
5819 
5820 /* Compressed closure does not apply closure permutation */
5821 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5822 {
5823   const PetscInt *cla = NULL;
5824   PetscInt        np, *pts = NULL;
5825 
5826   PetscFunctionBeginHot;
5827   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5828   if (!ornt && *clPoints) {
5829     PetscInt dof, off;
5830 
5831     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5832     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5833     PetscCall(ISGetIndices(*clPoints, &cla));
5834     np  = dof / 2;
5835     pts = (PetscInt *)&cla[off];
5836   } else {
5837     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
5838     PetscCall(CompressPoints_Private(section, &np, pts));
5839   }
5840   *numPoints = np;
5841   *points    = pts;
5842   *clp       = cla;
5843   PetscFunctionReturn(PETSC_SUCCESS);
5844 }
5845 
5846 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5847 {
5848   PetscFunctionBeginHot;
5849   if (!*clPoints) {
5850     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5851   } else {
5852     PetscCall(ISRestoreIndices(*clPoints, clp));
5853   }
5854   *numPoints = 0;
5855   *points    = NULL;
5856   *clSec     = NULL;
5857   *clPoints  = NULL;
5858   *clp       = NULL;
5859   PetscFunctionReturn(PETSC_SUCCESS);
5860 }
5861 
5862 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5863 {
5864   PetscInt            offset = 0, p;
5865   const PetscInt    **perms  = NULL;
5866   const PetscScalar **flips  = NULL;
5867 
5868   PetscFunctionBeginHot;
5869   *size = 0;
5870   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5871   for (p = 0; p < numPoints; p++) {
5872     const PetscInt     point = points[2 * p];
5873     const PetscInt    *perm  = perms ? perms[p] : NULL;
5874     const PetscScalar *flip  = flips ? flips[p] : NULL;
5875     PetscInt           dof, off, d;
5876     const PetscScalar *varr;
5877 
5878     PetscCall(PetscSectionGetDof(section, point, &dof));
5879     PetscCall(PetscSectionGetOffset(section, point, &off));
5880     varr = &vArray[off];
5881     if (clperm) {
5882       if (perm) {
5883         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5884       } else {
5885         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5886       }
5887       if (flip) {
5888         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5889       }
5890     } else {
5891       if (perm) {
5892         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5893       } else {
5894         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5895       }
5896       if (flip) {
5897         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5898       }
5899     }
5900     offset += dof;
5901   }
5902   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5903   *size = offset;
5904   PetscFunctionReturn(PETSC_SUCCESS);
5905 }
5906 
5907 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[])
5908 {
5909   PetscInt offset = 0, f;
5910 
5911   PetscFunctionBeginHot;
5912   *size = 0;
5913   for (f = 0; f < numFields; ++f) {
5914     PetscInt            p;
5915     const PetscInt    **perms = NULL;
5916     const PetscScalar **flips = NULL;
5917 
5918     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5919     for (p = 0; p < numPoints; p++) {
5920       const PetscInt     point = points[2 * p];
5921       PetscInt           fdof, foff, b;
5922       const PetscScalar *varr;
5923       const PetscInt    *perm = perms ? perms[p] : NULL;
5924       const PetscScalar *flip = flips ? flips[p] : NULL;
5925 
5926       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5927       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5928       varr = &vArray[foff];
5929       if (clperm) {
5930         if (perm) {
5931           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5932         } else {
5933           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5934         }
5935         if (flip) {
5936           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5937         }
5938       } else {
5939         if (perm) {
5940           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5941         } else {
5942           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5943         }
5944         if (flip) {
5945           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5946         }
5947       }
5948       offset += fdof;
5949     }
5950     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5951   }
5952   *size = offset;
5953   PetscFunctionReturn(PETSC_SUCCESS);
5954 }
5955 
5956 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
5957 {
5958   PetscSection    clSection;
5959   IS              clPoints;
5960   PetscInt       *points = NULL;
5961   const PetscInt *clp, *perm;
5962   PetscInt        depth, numFields, numPoints, asize;
5963 
5964   PetscFunctionBeginHot;
5965   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5966   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5967   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5968   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5969   PetscCall(DMPlexGetDepth(dm, &depth));
5970   PetscCall(PetscSectionGetNumFields(section, &numFields));
5971   if (depth == 1 && numFields < 2) {
5972     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5973     PetscFunctionReturn(PETSC_SUCCESS);
5974   }
5975   /* Get points */
5976   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
5977   /* Get sizes */
5978   asize = 0;
5979   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
5980     PetscInt dof;
5981     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5982     asize += dof;
5983   }
5984   if (values) {
5985     const PetscScalar *vArray;
5986     PetscInt           size;
5987 
5988     if (*values) {
5989       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);
5990     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5991     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
5992     PetscCall(VecGetArrayRead(v, &vArray));
5993     /* Get values */
5994     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5995     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5996     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5997     /* Cleanup array */
5998     PetscCall(VecRestoreArrayRead(v, &vArray));
5999   }
6000   if (csize) *csize = asize;
6001   /* Cleanup points */
6002   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6003   PetscFunctionReturn(PETSC_SUCCESS);
6004 }
6005 
6006 /*@C
6007   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6008 
6009   Not collective
6010 
6011   Input Parameters:
6012 + dm - The `DM`
6013 . section - The section describing the layout in `v`, or `NULL` to use the default section
6014 . v - The local vector
6015 - point - The point in the `DM`
6016 
6017   Input/Output Parameters:
6018 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6019 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6020            if the user provided `NULL`, it is a borrowed array and should not be freed
6021 
6022   Level: intermediate
6023 
6024   Notes:
6025   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6026   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6027   assembly function, and a user may already have allocated storage for this operation.
6028 
6029   A typical use could be
6030 .vb
6031    values = NULL;
6032    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6033    for (cl = 0; cl < clSize; ++cl) {
6034      <Compute on closure>
6035    }
6036    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6037 .ve
6038   or
6039 .vb
6040    PetscMalloc1(clMaxSize, &values);
6041    for (p = pStart; p < pEnd; ++p) {
6042      clSize = clMaxSize;
6043      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6044      for (cl = 0; cl < clSize; ++cl) {
6045        <Compute on closure>
6046      }
6047    }
6048    PetscFree(values);
6049 .ve
6050 
6051   Fortran Note:
6052   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6053 
6054 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6055 @*/
6056 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6057 {
6058   PetscFunctionBeginHot;
6059   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, v, point, 0, csize, values));
6060   PetscFunctionReturn(PETSC_SUCCESS);
6061 }
6062 
6063 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6064 {
6065   DMLabel            depthLabel;
6066   PetscSection       clSection;
6067   IS                 clPoints;
6068   PetscScalar       *array;
6069   const PetscScalar *vArray;
6070   PetscInt          *points = NULL;
6071   const PetscInt    *clp, *perm = NULL;
6072   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6073 
6074   PetscFunctionBeginHot;
6075   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6076   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6077   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6078   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6079   PetscCall(DMPlexGetDepth(dm, &mdepth));
6080   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6081   PetscCall(PetscSectionGetNumFields(section, &numFields));
6082   if (mdepth == 1 && numFields < 2) {
6083     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6084     PetscFunctionReturn(PETSC_SUCCESS);
6085   }
6086   /* Get points */
6087   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6088   for (clsize = 0, p = 0; p < Np; p++) {
6089     PetscInt dof;
6090     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6091     clsize += dof;
6092   }
6093   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6094   /* Filter points */
6095   for (p = 0; p < numPoints * 2; p += 2) {
6096     PetscInt dep;
6097 
6098     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6099     if (dep != depth) continue;
6100     points[Np * 2 + 0] = points[p];
6101     points[Np * 2 + 1] = points[p + 1];
6102     ++Np;
6103   }
6104   /* Get array */
6105   if (!values || !*values) {
6106     PetscInt asize = 0, dof;
6107 
6108     for (p = 0; p < Np * 2; p += 2) {
6109       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6110       asize += dof;
6111     }
6112     if (!values) {
6113       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6114       if (csize) *csize = asize;
6115       PetscFunctionReturn(PETSC_SUCCESS);
6116     }
6117     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6118   } else {
6119     array = *values;
6120   }
6121   PetscCall(VecGetArrayRead(v, &vArray));
6122   /* Get values */
6123   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6124   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6125   /* Cleanup points */
6126   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6127   /* Cleanup array */
6128   PetscCall(VecRestoreArrayRead(v, &vArray));
6129   if (!*values) {
6130     if (csize) *csize = size;
6131     *values = array;
6132   } else {
6133     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6134     *csize = size;
6135   }
6136   PetscFunctionReturn(PETSC_SUCCESS);
6137 }
6138 
6139 /*@C
6140   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6141 
6142   Not collective
6143 
6144   Input Parameters:
6145 + dm - The `DM`
6146 . section - The section describing the layout in `v`, or `NULL` to use the default section
6147 . v - The local vector
6148 . point - The point in the `DM`
6149 . csize - The number of values in the closure, or `NULL`
6150 - values - The array of values, which is a borrowed array and should not be freed
6151 
6152   Level: intermediate
6153 
6154   Note:
6155   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6156 
6157   Fortran Note:
6158   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6159 
6160 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6161 @*/
6162 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6163 {
6164   PetscInt size = 0;
6165 
6166   PetscFunctionBegin;
6167   /* Should work without recalculating size */
6168   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6169   *values = NULL;
6170   PetscFunctionReturn(PETSC_SUCCESS);
6171 }
6172 
6173 static inline void add(PetscScalar *x, PetscScalar y)
6174 {
6175   *x += y;
6176 }
6177 static inline void insert(PetscScalar *x, PetscScalar y)
6178 {
6179   *x = y;
6180 }
6181 
6182 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[])
6183 {
6184   PetscInt        cdof;  /* The number of constraints on this point */
6185   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6186   PetscScalar    *a;
6187   PetscInt        off, cind = 0, k;
6188 
6189   PetscFunctionBegin;
6190   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6191   PetscCall(PetscSectionGetOffset(section, point, &off));
6192   a = &array[off];
6193   if (!cdof || setBC) {
6194     if (clperm) {
6195       if (perm) {
6196         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6197       } else {
6198         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6199       }
6200     } else {
6201       if (perm) {
6202         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6203       } else {
6204         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6205       }
6206     }
6207   } else {
6208     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6209     if (clperm) {
6210       if (perm) {
6211         for (k = 0; k < dof; ++k) {
6212           if ((cind < cdof) && (k == cdofs[cind])) {
6213             ++cind;
6214             continue;
6215           }
6216           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6217         }
6218       } else {
6219         for (k = 0; k < dof; ++k) {
6220           if ((cind < cdof) && (k == cdofs[cind])) {
6221             ++cind;
6222             continue;
6223           }
6224           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6225         }
6226       }
6227     } else {
6228       if (perm) {
6229         for (k = 0; k < dof; ++k) {
6230           if ((cind < cdof) && (k == cdofs[cind])) {
6231             ++cind;
6232             continue;
6233           }
6234           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6235         }
6236       } else {
6237         for (k = 0; k < dof; ++k) {
6238           if ((cind < cdof) && (k == cdofs[cind])) {
6239             ++cind;
6240             continue;
6241           }
6242           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6243         }
6244       }
6245     }
6246   }
6247   PetscFunctionReturn(PETSC_SUCCESS);
6248 }
6249 
6250 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[])
6251 {
6252   PetscInt        cdof;  /* The number of constraints on this point */
6253   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6254   PetscScalar    *a;
6255   PetscInt        off, cind = 0, k;
6256 
6257   PetscFunctionBegin;
6258   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6259   PetscCall(PetscSectionGetOffset(section, point, &off));
6260   a = &array[off];
6261   if (cdof) {
6262     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6263     if (clperm) {
6264       if (perm) {
6265         for (k = 0; k < dof; ++k) {
6266           if ((cind < cdof) && (k == cdofs[cind])) {
6267             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6268             cind++;
6269           }
6270         }
6271       } else {
6272         for (k = 0; k < dof; ++k) {
6273           if ((cind < cdof) && (k == cdofs[cind])) {
6274             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6275             cind++;
6276           }
6277         }
6278       }
6279     } else {
6280       if (perm) {
6281         for (k = 0; k < dof; ++k) {
6282           if ((cind < cdof) && (k == cdofs[cind])) {
6283             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6284             cind++;
6285           }
6286         }
6287       } else {
6288         for (k = 0; k < dof; ++k) {
6289           if ((cind < cdof) && (k == cdofs[cind])) {
6290             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6291             cind++;
6292           }
6293         }
6294       }
6295     }
6296   }
6297   PetscFunctionReturn(PETSC_SUCCESS);
6298 }
6299 
6300 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[])
6301 {
6302   PetscScalar    *a;
6303   PetscInt        fdof, foff, fcdof, foffset = *offset;
6304   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6305   PetscInt        cind = 0, b;
6306 
6307   PetscFunctionBegin;
6308   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6309   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6310   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6311   a = &array[foff];
6312   if (!fcdof || setBC) {
6313     if (clperm) {
6314       if (perm) {
6315         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6316       } else {
6317         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6318       }
6319     } else {
6320       if (perm) {
6321         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6322       } else {
6323         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6324       }
6325     }
6326   } else {
6327     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6328     if (clperm) {
6329       if (perm) {
6330         for (b = 0; b < fdof; b++) {
6331           if ((cind < fcdof) && (b == fcdofs[cind])) {
6332             ++cind;
6333             continue;
6334           }
6335           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6336         }
6337       } else {
6338         for (b = 0; b < fdof; b++) {
6339           if ((cind < fcdof) && (b == fcdofs[cind])) {
6340             ++cind;
6341             continue;
6342           }
6343           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6344         }
6345       }
6346     } else {
6347       if (perm) {
6348         for (b = 0; b < fdof; b++) {
6349           if ((cind < fcdof) && (b == fcdofs[cind])) {
6350             ++cind;
6351             continue;
6352           }
6353           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6354         }
6355       } else {
6356         for (b = 0; b < fdof; b++) {
6357           if ((cind < fcdof) && (b == fcdofs[cind])) {
6358             ++cind;
6359             continue;
6360           }
6361           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6362         }
6363       }
6364     }
6365   }
6366   *offset += fdof;
6367   PetscFunctionReturn(PETSC_SUCCESS);
6368 }
6369 
6370 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[])
6371 {
6372   PetscScalar    *a;
6373   PetscInt        fdof, foff, fcdof, foffset = *offset;
6374   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6375   PetscInt        Nc, cind = 0, ncind = 0, b;
6376   PetscBool       ncSet, fcSet;
6377 
6378   PetscFunctionBegin;
6379   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6380   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6381   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6382   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6383   a = &array[foff];
6384   if (fcdof) {
6385     /* We just override fcdof and fcdofs with Ncc and comps */
6386     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6387     if (clperm) {
6388       if (perm) {
6389         if (comps) {
6390           for (b = 0; b < fdof; b++) {
6391             ncSet = fcSet = PETSC_FALSE;
6392             if (b % Nc == comps[ncind]) {
6393               ncind = (ncind + 1) % Ncc;
6394               ncSet = PETSC_TRUE;
6395             }
6396             if ((cind < fcdof) && (b == fcdofs[cind])) {
6397               ++cind;
6398               fcSet = PETSC_TRUE;
6399             }
6400             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6401           }
6402         } else {
6403           for (b = 0; b < fdof; b++) {
6404             if ((cind < fcdof) && (b == fcdofs[cind])) {
6405               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6406               ++cind;
6407             }
6408           }
6409         }
6410       } else {
6411         if (comps) {
6412           for (b = 0; b < fdof; b++) {
6413             ncSet = fcSet = PETSC_FALSE;
6414             if (b % Nc == comps[ncind]) {
6415               ncind = (ncind + 1) % Ncc;
6416               ncSet = PETSC_TRUE;
6417             }
6418             if ((cind < fcdof) && (b == fcdofs[cind])) {
6419               ++cind;
6420               fcSet = PETSC_TRUE;
6421             }
6422             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6423           }
6424         } else {
6425           for (b = 0; b < fdof; b++) {
6426             if ((cind < fcdof) && (b == fcdofs[cind])) {
6427               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6428               ++cind;
6429             }
6430           }
6431         }
6432       }
6433     } else {
6434       if (perm) {
6435         if (comps) {
6436           for (b = 0; b < fdof; b++) {
6437             ncSet = fcSet = PETSC_FALSE;
6438             if (b % Nc == comps[ncind]) {
6439               ncind = (ncind + 1) % Ncc;
6440               ncSet = PETSC_TRUE;
6441             }
6442             if ((cind < fcdof) && (b == fcdofs[cind])) {
6443               ++cind;
6444               fcSet = PETSC_TRUE;
6445             }
6446             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6447           }
6448         } else {
6449           for (b = 0; b < fdof; b++) {
6450             if ((cind < fcdof) && (b == fcdofs[cind])) {
6451               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6452               ++cind;
6453             }
6454           }
6455         }
6456       } else {
6457         if (comps) {
6458           for (b = 0; b < fdof; b++) {
6459             ncSet = fcSet = PETSC_FALSE;
6460             if (b % Nc == comps[ncind]) {
6461               ncind = (ncind + 1) % Ncc;
6462               ncSet = PETSC_TRUE;
6463             }
6464             if ((cind < fcdof) && (b == fcdofs[cind])) {
6465               ++cind;
6466               fcSet = PETSC_TRUE;
6467             }
6468             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6469           }
6470         } else {
6471           for (b = 0; b < fdof; b++) {
6472             if ((cind < fcdof) && (b == fcdofs[cind])) {
6473               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6474               ++cind;
6475             }
6476           }
6477         }
6478       }
6479     }
6480   }
6481   *offset += fdof;
6482   PetscFunctionReturn(PETSC_SUCCESS);
6483 }
6484 
6485 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6486 {
6487   PetscScalar    *array;
6488   const PetscInt *cone, *coneO;
6489   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6490 
6491   PetscFunctionBeginHot;
6492   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6493   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6494   PetscCall(DMPlexGetCone(dm, point, &cone));
6495   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6496   PetscCall(VecGetArray(v, &array));
6497   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6498     const PetscInt cp = !p ? point : cone[p - 1];
6499     const PetscInt o  = !p ? 0 : coneO[p - 1];
6500 
6501     if ((cp < pStart) || (cp >= pEnd)) {
6502       dof = 0;
6503       continue;
6504     }
6505     PetscCall(PetscSectionGetDof(section, cp, &dof));
6506     /* ADD_VALUES */
6507     {
6508       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6509       PetscScalar    *a;
6510       PetscInt        cdof, coff, cind = 0, k;
6511 
6512       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6513       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6514       a = &array[coff];
6515       if (!cdof) {
6516         if (o >= 0) {
6517           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6518         } else {
6519           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6520         }
6521       } else {
6522         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6523         if (o >= 0) {
6524           for (k = 0; k < dof; ++k) {
6525             if ((cind < cdof) && (k == cdofs[cind])) {
6526               ++cind;
6527               continue;
6528             }
6529             a[k] += values[off + k];
6530           }
6531         } else {
6532           for (k = 0; k < dof; ++k) {
6533             if ((cind < cdof) && (k == cdofs[cind])) {
6534               ++cind;
6535               continue;
6536             }
6537             a[k] += values[off + dof - k - 1];
6538           }
6539         }
6540       }
6541     }
6542   }
6543   PetscCall(VecRestoreArray(v, &array));
6544   PetscFunctionReturn(PETSC_SUCCESS);
6545 }
6546 
6547 /*@C
6548   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6549 
6550   Not collective
6551 
6552   Input Parameters:
6553 + dm - The `DM`
6554 . section - The section describing the layout in `v`, or `NULL` to use the default section
6555 . v - The local vector
6556 . point - The point in the `DM`
6557 . values - The array of values
6558 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6559          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6560 
6561   Level: intermediate
6562 
6563 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6564 @*/
6565 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6566 {
6567   PetscSection    clSection;
6568   IS              clPoints;
6569   PetscScalar    *array;
6570   PetscInt       *points = NULL;
6571   const PetscInt *clp, *clperm = NULL;
6572   PetscInt        depth, numFields, numPoints, p, clsize;
6573 
6574   PetscFunctionBeginHot;
6575   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6576   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6577   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6578   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6579   PetscCall(DMPlexGetDepth(dm, &depth));
6580   PetscCall(PetscSectionGetNumFields(section, &numFields));
6581   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6582     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6583     PetscFunctionReturn(PETSC_SUCCESS);
6584   }
6585   /* Get points */
6586   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6587   for (clsize = 0, p = 0; p < numPoints; p++) {
6588     PetscInt dof;
6589     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6590     clsize += dof;
6591   }
6592   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6593   /* Get array */
6594   PetscCall(VecGetArray(v, &array));
6595   /* Get values */
6596   if (numFields > 0) {
6597     PetscInt offset = 0, f;
6598     for (f = 0; f < numFields; ++f) {
6599       const PetscInt    **perms = NULL;
6600       const PetscScalar **flips = NULL;
6601 
6602       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6603       switch (mode) {
6604       case INSERT_VALUES:
6605         for (p = 0; p < numPoints; p++) {
6606           const PetscInt     point = points[2 * p];
6607           const PetscInt    *perm  = perms ? perms[p] : NULL;
6608           const PetscScalar *flip  = flips ? flips[p] : NULL;
6609           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6610         }
6611         break;
6612       case INSERT_ALL_VALUES:
6613         for (p = 0; p < numPoints; p++) {
6614           const PetscInt     point = points[2 * p];
6615           const PetscInt    *perm  = perms ? perms[p] : NULL;
6616           const PetscScalar *flip  = flips ? flips[p] : NULL;
6617           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6618         }
6619         break;
6620       case INSERT_BC_VALUES:
6621         for (p = 0; p < numPoints; p++) {
6622           const PetscInt     point = points[2 * p];
6623           const PetscInt    *perm  = perms ? perms[p] : NULL;
6624           const PetscScalar *flip  = flips ? flips[p] : NULL;
6625           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6626         }
6627         break;
6628       case ADD_VALUES:
6629         for (p = 0; p < numPoints; p++) {
6630           const PetscInt     point = points[2 * p];
6631           const PetscInt    *perm  = perms ? perms[p] : NULL;
6632           const PetscScalar *flip  = flips ? flips[p] : NULL;
6633           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6634         }
6635         break;
6636       case ADD_ALL_VALUES:
6637         for (p = 0; p < numPoints; p++) {
6638           const PetscInt     point = points[2 * p];
6639           const PetscInt    *perm  = perms ? perms[p] : NULL;
6640           const PetscScalar *flip  = flips ? flips[p] : NULL;
6641           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6642         }
6643         break;
6644       case ADD_BC_VALUES:
6645         for (p = 0; p < numPoints; p++) {
6646           const PetscInt     point = points[2 * p];
6647           const PetscInt    *perm  = perms ? perms[p] : NULL;
6648           const PetscScalar *flip  = flips ? flips[p] : NULL;
6649           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6650         }
6651         break;
6652       default:
6653         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6654       }
6655       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6656     }
6657   } else {
6658     PetscInt            dof, off;
6659     const PetscInt    **perms = NULL;
6660     const PetscScalar **flips = NULL;
6661 
6662     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6663     switch (mode) {
6664     case INSERT_VALUES:
6665       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6666         const PetscInt     point = points[2 * p];
6667         const PetscInt    *perm  = perms ? perms[p] : NULL;
6668         const PetscScalar *flip  = flips ? flips[p] : NULL;
6669         PetscCall(PetscSectionGetDof(section, point, &dof));
6670         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6671       }
6672       break;
6673     case INSERT_ALL_VALUES:
6674       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6675         const PetscInt     point = points[2 * p];
6676         const PetscInt    *perm  = perms ? perms[p] : NULL;
6677         const PetscScalar *flip  = flips ? flips[p] : NULL;
6678         PetscCall(PetscSectionGetDof(section, point, &dof));
6679         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6680       }
6681       break;
6682     case INSERT_BC_VALUES:
6683       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6684         const PetscInt     point = points[2 * p];
6685         const PetscInt    *perm  = perms ? perms[p] : NULL;
6686         const PetscScalar *flip  = flips ? flips[p] : NULL;
6687         PetscCall(PetscSectionGetDof(section, point, &dof));
6688         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6689       }
6690       break;
6691     case ADD_VALUES:
6692       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6693         const PetscInt     point = points[2 * p];
6694         const PetscInt    *perm  = perms ? perms[p] : NULL;
6695         const PetscScalar *flip  = flips ? flips[p] : NULL;
6696         PetscCall(PetscSectionGetDof(section, point, &dof));
6697         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6698       }
6699       break;
6700     case ADD_ALL_VALUES:
6701       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6702         const PetscInt     point = points[2 * p];
6703         const PetscInt    *perm  = perms ? perms[p] : NULL;
6704         const PetscScalar *flip  = flips ? flips[p] : NULL;
6705         PetscCall(PetscSectionGetDof(section, point, &dof));
6706         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6707       }
6708       break;
6709     case ADD_BC_VALUES:
6710       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6711         const PetscInt     point = points[2 * p];
6712         const PetscInt    *perm  = perms ? perms[p] : NULL;
6713         const PetscScalar *flip  = flips ? flips[p] : NULL;
6714         PetscCall(PetscSectionGetDof(section, point, &dof));
6715         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6716       }
6717       break;
6718     default:
6719       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6720     }
6721     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6722   }
6723   /* Cleanup points */
6724   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6725   /* Cleanup array */
6726   PetscCall(VecRestoreArray(v, &array));
6727   PetscFunctionReturn(PETSC_SUCCESS);
6728 }
6729 
6730 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6731 {
6732   const PetscInt *supp, *cone;
6733   PetscScalar    *a;
6734   PetscInt        dim, Ns, dof, off, n = 0;
6735 
6736   PetscFunctionBegin;
6737   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6738   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6739   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6740   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6741   if (PetscDefined(USE_DEBUG)) {
6742     PetscInt vStart, vEnd;
6743 
6744     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6745     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);
6746   }
6747   PetscValidScalarPointer(values, 5);
6748 
6749   PetscCall(DMGetDimension(dm, &dim));
6750   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6751   PetscCall(DMPlexGetSupport(dm, point, &supp));
6752   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);
6753   PetscCall(VecGetArray(v, &a));
6754   PetscCall(PetscSectionGetDof(section, point, &dof));
6755   PetscCall(PetscSectionGetOffset(section, point, &off));
6756   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6757   for (PetscInt d = 0; d < dim; ++d) {
6758     // Left edge
6759     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6760     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6761     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6762     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6763     // Right edge
6764     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6765     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6766     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6767     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6768   }
6769   PetscCall(VecRestoreArray(v, &a));
6770   PetscFunctionReturn(PETSC_SUCCESS);
6771 }
6772 
6773 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6774 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6775 {
6776   PetscFunctionBegin;
6777   *contains = PETSC_TRUE;
6778   if (label) {
6779     PetscInt fdof;
6780 
6781     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6782     if (!*contains) {
6783       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6784       *offset += fdof;
6785       PetscFunctionReturn(PETSC_SUCCESS);
6786     }
6787   }
6788   PetscFunctionReturn(PETSC_SUCCESS);
6789 }
6790 
6791 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6792 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)
6793 {
6794   PetscSection    clSection;
6795   IS              clPoints;
6796   PetscScalar    *array;
6797   PetscInt       *points = NULL;
6798   const PetscInt *clp;
6799   PetscInt        numFields, numPoints, p;
6800   PetscInt        offset = 0, f;
6801 
6802   PetscFunctionBeginHot;
6803   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6804   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6805   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6806   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6807   PetscCall(PetscSectionGetNumFields(section, &numFields));
6808   /* Get points */
6809   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6810   /* Get array */
6811   PetscCall(VecGetArray(v, &array));
6812   /* Get values */
6813   for (f = 0; f < numFields; ++f) {
6814     const PetscInt    **perms = NULL;
6815     const PetscScalar **flips = NULL;
6816     PetscBool           contains;
6817 
6818     if (!fieldActive[f]) {
6819       for (p = 0; p < numPoints * 2; p += 2) {
6820         PetscInt fdof;
6821         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6822         offset += fdof;
6823       }
6824       continue;
6825     }
6826     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6827     switch (mode) {
6828     case INSERT_VALUES:
6829       for (p = 0; p < numPoints; p++) {
6830         const PetscInt     point = points[2 * p];
6831         const PetscInt    *perm  = perms ? perms[p] : NULL;
6832         const PetscScalar *flip  = flips ? flips[p] : NULL;
6833         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6834         if (!contains) continue;
6835         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6836       }
6837       break;
6838     case INSERT_ALL_VALUES:
6839       for (p = 0; p < numPoints; p++) {
6840         const PetscInt     point = points[2 * p];
6841         const PetscInt    *perm  = perms ? perms[p] : NULL;
6842         const PetscScalar *flip  = flips ? flips[p] : NULL;
6843         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6844         if (!contains) continue;
6845         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6846       }
6847       break;
6848     case INSERT_BC_VALUES:
6849       for (p = 0; p < numPoints; p++) {
6850         const PetscInt     point = points[2 * p];
6851         const PetscInt    *perm  = perms ? perms[p] : NULL;
6852         const PetscScalar *flip  = flips ? flips[p] : NULL;
6853         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6854         if (!contains) continue;
6855         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6856       }
6857       break;
6858     case ADD_VALUES:
6859       for (p = 0; p < numPoints; p++) {
6860         const PetscInt     point = points[2 * p];
6861         const PetscInt    *perm  = perms ? perms[p] : NULL;
6862         const PetscScalar *flip  = flips ? flips[p] : NULL;
6863         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6864         if (!contains) continue;
6865         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6866       }
6867       break;
6868     case ADD_ALL_VALUES:
6869       for (p = 0; p < numPoints; p++) {
6870         const PetscInt     point = points[2 * p];
6871         const PetscInt    *perm  = perms ? perms[p] : NULL;
6872         const PetscScalar *flip  = flips ? flips[p] : NULL;
6873         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6874         if (!contains) continue;
6875         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6876       }
6877       break;
6878     default:
6879       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6880     }
6881     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6882   }
6883   /* Cleanup points */
6884   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6885   /* Cleanup array */
6886   PetscCall(VecRestoreArray(v, &array));
6887   PetscFunctionReturn(PETSC_SUCCESS);
6888 }
6889 
6890 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6891 {
6892   PetscMPIInt rank;
6893   PetscInt    i, j;
6894 
6895   PetscFunctionBegin;
6896   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6897   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6898   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6899   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6900   numCIndices = numCIndices ? numCIndices : numRIndices;
6901   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6902   for (i = 0; i < numRIndices; i++) {
6903     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6904     for (j = 0; j < numCIndices; j++) {
6905 #if defined(PETSC_USE_COMPLEX)
6906       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6907 #else
6908       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6909 #endif
6910     }
6911     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6912   }
6913   PetscFunctionReturn(PETSC_SUCCESS);
6914 }
6915 
6916 /*
6917   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6918 
6919   Input Parameters:
6920 + section - The section for this data layout
6921 . islocal - Is the section (and thus indices being requested) local or global?
6922 . point   - The point contributing dofs with these indices
6923 . off     - The global offset of this point
6924 . loff    - The local offset of each field
6925 . setBC   - The flag determining whether to include indices of boundary values
6926 . perm    - A permutation of the dofs on this point, or NULL
6927 - indperm - A permutation of the entire indices array, or NULL
6928 
6929   Output Parameter:
6930 . indices - Indices for dofs on this point
6931 
6932   Level: developer
6933 
6934   Note: The indices could be local or global, depending on the value of 'off'.
6935 */
6936 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6937 {
6938   PetscInt        dof;   /* The number of unknowns on this point */
6939   PetscInt        cdof;  /* The number of constraints on this point */
6940   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6941   PetscInt        cind = 0, k;
6942 
6943   PetscFunctionBegin;
6944   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6945   PetscCall(PetscSectionGetDof(section, point, &dof));
6946   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6947   if (!cdof || setBC) {
6948     for (k = 0; k < dof; ++k) {
6949       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6950       const PetscInt ind    = indperm ? indperm[preind] : preind;
6951 
6952       indices[ind] = off + k;
6953     }
6954   } else {
6955     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6956     for (k = 0; k < dof; ++k) {
6957       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6958       const PetscInt ind    = indperm ? indperm[preind] : preind;
6959 
6960       if ((cind < cdof) && (k == cdofs[cind])) {
6961         /* Insert check for returning constrained indices */
6962         indices[ind] = -(off + k + 1);
6963         ++cind;
6964       } else {
6965         indices[ind] = off + k - (islocal ? 0 : cind);
6966       }
6967     }
6968   }
6969   *loff += dof;
6970   PetscFunctionReturn(PETSC_SUCCESS);
6971 }
6972 
6973 /*
6974  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6975 
6976  Input Parameters:
6977 + section - a section (global or local)
6978 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
6979 . point - point within section
6980 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6981 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6982 . setBC - identify constrained (boundary condition) points via involution.
6983 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6984 . permsoff - offset
6985 - indperm - index permutation
6986 
6987  Output Parameter:
6988 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6989 . indices - array to hold indices (as defined by section) of each dof associated with point
6990 
6991  Notes:
6992  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6993  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6994  in the local vector.
6995 
6996  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6997  significant).  It is invalid to call with a global section and setBC=true.
6998 
6999  Developer Note:
7000  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7001  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7002  offset could be obtained from the section instead of passing it explicitly as we do now.
7003 
7004  Example:
7005  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7006  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7007  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7008  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.
7009 
7010  Level: developer
7011 */
7012 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[])
7013 {
7014   PetscInt numFields, foff, f;
7015 
7016   PetscFunctionBegin;
7017   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7018   PetscCall(PetscSectionGetNumFields(section, &numFields));
7019   for (f = 0, foff = 0; f < numFields; ++f) {
7020     PetscInt        fdof, cfdof;
7021     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7022     PetscInt        cind = 0, b;
7023     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7024 
7025     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7026     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7027     if (!cfdof || setBC) {
7028       for (b = 0; b < fdof; ++b) {
7029         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7030         const PetscInt ind    = indperm ? indperm[preind] : preind;
7031 
7032         indices[ind] = off + foff + b;
7033       }
7034     } else {
7035       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7036       for (b = 0; b < fdof; ++b) {
7037         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7038         const PetscInt ind    = indperm ? indperm[preind] : preind;
7039 
7040         if ((cind < cfdof) && (b == fcdofs[cind])) {
7041           indices[ind] = -(off + foff + b + 1);
7042           ++cind;
7043         } else {
7044           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7045         }
7046       }
7047     }
7048     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7049     foffs[f] += fdof;
7050   }
7051   PetscFunctionReturn(PETSC_SUCCESS);
7052 }
7053 
7054 /*
7055   This version believes the globalSection offsets for each field, rather than just the point offset
7056 
7057  . foffs - The offset into 'indices' for each field, since it is segregated by field
7058 
7059  Notes:
7060  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7061  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7062 */
7063 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7064 {
7065   PetscInt numFields, foff, f;
7066 
7067   PetscFunctionBegin;
7068   PetscCall(PetscSectionGetNumFields(section, &numFields));
7069   for (f = 0; f < numFields; ++f) {
7070     PetscInt        fdof, cfdof;
7071     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7072     PetscInt        cind = 0, b;
7073     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7074 
7075     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7076     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7077     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7078     if (!cfdof) {
7079       for (b = 0; b < fdof; ++b) {
7080         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7081         const PetscInt ind    = indperm ? indperm[preind] : preind;
7082 
7083         indices[ind] = foff + b;
7084       }
7085     } else {
7086       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7087       for (b = 0; b < fdof; ++b) {
7088         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7089         const PetscInt ind    = indperm ? indperm[preind] : preind;
7090 
7091         if ((cind < cfdof) && (b == fcdofs[cind])) {
7092           indices[ind] = -(foff + b + 1);
7093           ++cind;
7094         } else {
7095           indices[ind] = foff + b - cind;
7096         }
7097       }
7098     }
7099     foffs[f] += fdof;
7100   }
7101   PetscFunctionReturn(PETSC_SUCCESS);
7102 }
7103 
7104 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)
7105 {
7106   Mat             cMat;
7107   PetscSection    aSec, cSec;
7108   IS              aIS;
7109   PetscInt        aStart = -1, aEnd = -1;
7110   const PetscInt *anchors;
7111   PetscInt        numFields, f, p, q, newP = 0;
7112   PetscInt        newNumPoints = 0, newNumIndices = 0;
7113   PetscInt       *newPoints, *indices, *newIndices;
7114   PetscInt        maxAnchor, maxDof;
7115   PetscInt        newOffsets[32];
7116   PetscInt       *pointMatOffsets[32];
7117   PetscInt       *newPointOffsets[32];
7118   PetscScalar    *pointMat[32];
7119   PetscScalar    *newValues      = NULL, *tmpValues;
7120   PetscBool       anyConstrained = PETSC_FALSE;
7121 
7122   PetscFunctionBegin;
7123   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7124   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7125   PetscCall(PetscSectionGetNumFields(section, &numFields));
7126 
7127   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7128   /* if there are point-to-point constraints */
7129   if (aSec) {
7130     PetscCall(PetscArrayzero(newOffsets, 32));
7131     PetscCall(ISGetIndices(aIS, &anchors));
7132     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7133     /* figure out how many points are going to be in the new element matrix
7134      * (we allow double counting, because it's all just going to be summed
7135      * into the global matrix anyway) */
7136     for (p = 0; p < 2 * numPoints; p += 2) {
7137       PetscInt b    = points[p];
7138       PetscInt bDof = 0, bSecDof;
7139 
7140       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7141       if (!bSecDof) continue;
7142       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7143       if (bDof) {
7144         /* this point is constrained */
7145         /* it is going to be replaced by its anchors */
7146         PetscInt bOff, q;
7147 
7148         anyConstrained = PETSC_TRUE;
7149         newNumPoints += bDof;
7150         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7151         for (q = 0; q < bDof; q++) {
7152           PetscInt a = anchors[bOff + q];
7153           PetscInt aDof;
7154 
7155           PetscCall(PetscSectionGetDof(section, a, &aDof));
7156           newNumIndices += aDof;
7157           for (f = 0; f < numFields; ++f) {
7158             PetscInt fDof;
7159 
7160             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7161             newOffsets[f + 1] += fDof;
7162           }
7163         }
7164       } else {
7165         /* this point is not constrained */
7166         newNumPoints++;
7167         newNumIndices += bSecDof;
7168         for (f = 0; f < numFields; ++f) {
7169           PetscInt fDof;
7170 
7171           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7172           newOffsets[f + 1] += fDof;
7173         }
7174       }
7175     }
7176   }
7177   if (!anyConstrained) {
7178     if (outNumPoints) *outNumPoints = 0;
7179     if (outNumIndices) *outNumIndices = 0;
7180     if (outPoints) *outPoints = NULL;
7181     if (outValues) *outValues = NULL;
7182     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7183     PetscFunctionReturn(PETSC_SUCCESS);
7184   }
7185 
7186   if (outNumPoints) *outNumPoints = newNumPoints;
7187   if (outNumIndices) *outNumIndices = newNumIndices;
7188 
7189   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7190 
7191   if (!outPoints && !outValues) {
7192     if (offsets) {
7193       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7194     }
7195     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7196     PetscFunctionReturn(PETSC_SUCCESS);
7197   }
7198 
7199   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7200 
7201   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7202 
7203   /* workspaces */
7204   if (numFields) {
7205     for (f = 0; f < numFields; f++) {
7206       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7207       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7208     }
7209   } else {
7210     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7211     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7212   }
7213 
7214   /* get workspaces for the point-to-point matrices */
7215   if (numFields) {
7216     PetscInt totalOffset, totalMatOffset;
7217 
7218     for (p = 0; p < numPoints; p++) {
7219       PetscInt b    = points[2 * p];
7220       PetscInt bDof = 0, bSecDof;
7221 
7222       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7223       if (!bSecDof) {
7224         for (f = 0; f < numFields; f++) {
7225           newPointOffsets[f][p + 1] = 0;
7226           pointMatOffsets[f][p + 1] = 0;
7227         }
7228         continue;
7229       }
7230       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7231       if (bDof) {
7232         for (f = 0; f < numFields; f++) {
7233           PetscInt fDof, q, bOff, allFDof = 0;
7234 
7235           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7236           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7237           for (q = 0; q < bDof; q++) {
7238             PetscInt a = anchors[bOff + q];
7239             PetscInt aFDof;
7240 
7241             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7242             allFDof += aFDof;
7243           }
7244           newPointOffsets[f][p + 1] = allFDof;
7245           pointMatOffsets[f][p + 1] = fDof * allFDof;
7246         }
7247       } else {
7248         for (f = 0; f < numFields; f++) {
7249           PetscInt fDof;
7250 
7251           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7252           newPointOffsets[f][p + 1] = fDof;
7253           pointMatOffsets[f][p + 1] = 0;
7254         }
7255       }
7256     }
7257     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7258       newPointOffsets[f][0] = totalOffset;
7259       pointMatOffsets[f][0] = totalMatOffset;
7260       for (p = 0; p < numPoints; p++) {
7261         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7262         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7263       }
7264       totalOffset    = newPointOffsets[f][numPoints];
7265       totalMatOffset = pointMatOffsets[f][numPoints];
7266       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7267     }
7268   } else {
7269     for (p = 0; p < numPoints; p++) {
7270       PetscInt b    = points[2 * p];
7271       PetscInt bDof = 0, bSecDof;
7272 
7273       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7274       if (!bSecDof) {
7275         newPointOffsets[0][p + 1] = 0;
7276         pointMatOffsets[0][p + 1] = 0;
7277         continue;
7278       }
7279       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7280       if (bDof) {
7281         PetscInt bOff, q, allDof = 0;
7282 
7283         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7284         for (q = 0; q < bDof; q++) {
7285           PetscInt a = anchors[bOff + q], aDof;
7286 
7287           PetscCall(PetscSectionGetDof(section, a, &aDof));
7288           allDof += aDof;
7289         }
7290         newPointOffsets[0][p + 1] = allDof;
7291         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7292       } else {
7293         newPointOffsets[0][p + 1] = bSecDof;
7294         pointMatOffsets[0][p + 1] = 0;
7295       }
7296     }
7297     newPointOffsets[0][0] = 0;
7298     pointMatOffsets[0][0] = 0;
7299     for (p = 0; p < numPoints; p++) {
7300       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7301       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7302     }
7303     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7304   }
7305 
7306   /* output arrays */
7307   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7308 
7309   /* get the point-to-point matrices; construct newPoints */
7310   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7311   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7312   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7313   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7314   if (numFields) {
7315     for (p = 0, newP = 0; p < numPoints; p++) {
7316       PetscInt b    = points[2 * p];
7317       PetscInt o    = points[2 * p + 1];
7318       PetscInt bDof = 0, bSecDof;
7319 
7320       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7321       if (!bSecDof) continue;
7322       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7323       if (bDof) {
7324         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7325 
7326         fStart[0] = 0;
7327         fEnd[0]   = 0;
7328         for (f = 0; f < numFields; f++) {
7329           PetscInt fDof;
7330 
7331           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7332           fStart[f + 1] = fStart[f] + fDof;
7333           fEnd[f + 1]   = fStart[f + 1];
7334         }
7335         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7336         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7337 
7338         fAnchorStart[0] = 0;
7339         fAnchorEnd[0]   = 0;
7340         for (f = 0; f < numFields; f++) {
7341           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7342 
7343           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7344           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7345         }
7346         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7347         for (q = 0; q < bDof; q++) {
7348           PetscInt a = anchors[bOff + q], aOff;
7349 
7350           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7351           newPoints[2 * (newP + q)]     = a;
7352           newPoints[2 * (newP + q) + 1] = 0;
7353           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7354           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7355         }
7356         newP += bDof;
7357 
7358         if (outValues) {
7359           /* get the point-to-point submatrix */
7360           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]));
7361         }
7362       } else {
7363         newPoints[2 * newP]     = b;
7364         newPoints[2 * newP + 1] = o;
7365         newP++;
7366       }
7367     }
7368   } else {
7369     for (p = 0; p < numPoints; p++) {
7370       PetscInt b    = points[2 * p];
7371       PetscInt o    = points[2 * p + 1];
7372       PetscInt bDof = 0, bSecDof;
7373 
7374       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7375       if (!bSecDof) continue;
7376       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7377       if (bDof) {
7378         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7379 
7380         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7381         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7382 
7383         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7384         for (q = 0; q < bDof; q++) {
7385           PetscInt a = anchors[bOff + q], aOff;
7386 
7387           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7388 
7389           newPoints[2 * (newP + q)]     = a;
7390           newPoints[2 * (newP + q) + 1] = 0;
7391           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7392           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7393         }
7394         newP += bDof;
7395 
7396         /* get the point-to-point submatrix */
7397         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7398       } else {
7399         newPoints[2 * newP]     = b;
7400         newPoints[2 * newP + 1] = o;
7401         newP++;
7402       }
7403     }
7404   }
7405 
7406   if (outValues) {
7407     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7408     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7409     /* multiply constraints on the right */
7410     if (numFields) {
7411       for (f = 0; f < numFields; f++) {
7412         PetscInt oldOff = offsets[f];
7413 
7414         for (p = 0; p < numPoints; p++) {
7415           PetscInt cStart = newPointOffsets[f][p];
7416           PetscInt b      = points[2 * p];
7417           PetscInt c, r, k;
7418           PetscInt dof;
7419 
7420           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7421           if (!dof) continue;
7422           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7423             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7424             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7425 
7426             for (r = 0; r < numIndices; r++) {
7427               for (c = 0; c < nCols; c++) {
7428                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7429               }
7430             }
7431           } else {
7432             /* copy this column as is */
7433             for (r = 0; r < numIndices; r++) {
7434               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7435             }
7436           }
7437           oldOff += dof;
7438         }
7439       }
7440     } else {
7441       PetscInt oldOff = 0;
7442       for (p = 0; p < numPoints; p++) {
7443         PetscInt cStart = newPointOffsets[0][p];
7444         PetscInt b      = points[2 * p];
7445         PetscInt c, r, k;
7446         PetscInt dof;
7447 
7448         PetscCall(PetscSectionGetDof(section, b, &dof));
7449         if (!dof) continue;
7450         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7451           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7452           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7453 
7454           for (r = 0; r < numIndices; r++) {
7455             for (c = 0; c < nCols; c++) {
7456               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7457             }
7458           }
7459         } else {
7460           /* copy this column as is */
7461           for (r = 0; r < numIndices; r++) {
7462             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7463           }
7464         }
7465         oldOff += dof;
7466       }
7467     }
7468 
7469     if (multiplyLeft) {
7470       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7471       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7472       /* multiply constraints transpose on the left */
7473       if (numFields) {
7474         for (f = 0; f < numFields; f++) {
7475           PetscInt oldOff = offsets[f];
7476 
7477           for (p = 0; p < numPoints; p++) {
7478             PetscInt rStart = newPointOffsets[f][p];
7479             PetscInt b      = points[2 * p];
7480             PetscInt c, r, k;
7481             PetscInt dof;
7482 
7483             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7484             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7485               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7486               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7487 
7488               for (r = 0; r < nRows; r++) {
7489                 for (c = 0; c < newNumIndices; c++) {
7490                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7491                 }
7492               }
7493             } else {
7494               /* copy this row as is */
7495               for (r = 0; r < dof; r++) {
7496                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7497               }
7498             }
7499             oldOff += dof;
7500           }
7501         }
7502       } else {
7503         PetscInt oldOff = 0;
7504 
7505         for (p = 0; p < numPoints; p++) {
7506           PetscInt rStart = newPointOffsets[0][p];
7507           PetscInt b      = points[2 * p];
7508           PetscInt c, r, k;
7509           PetscInt dof;
7510 
7511           PetscCall(PetscSectionGetDof(section, b, &dof));
7512           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7513             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7514             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7515 
7516             for (r = 0; r < nRows; r++) {
7517               for (c = 0; c < newNumIndices; c++) {
7518                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7519               }
7520             }
7521           } else {
7522             /* copy this row as is */
7523             for (r = 0; r < dof; r++) {
7524               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7525             }
7526           }
7527           oldOff += dof;
7528         }
7529       }
7530 
7531       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7532     } else {
7533       newValues = tmpValues;
7534     }
7535   }
7536 
7537   /* clean up */
7538   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7539   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7540 
7541   if (numFields) {
7542     for (f = 0; f < numFields; f++) {
7543       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7544       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7545       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7546     }
7547   } else {
7548     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7549     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7550     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7551   }
7552   PetscCall(ISRestoreIndices(aIS, &anchors));
7553 
7554   /* output */
7555   if (outPoints) {
7556     *outPoints = newPoints;
7557   } else {
7558     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7559   }
7560   if (outValues) *outValues = newValues;
7561   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7562   PetscFunctionReturn(PETSC_SUCCESS);
7563 }
7564 
7565 /*@C
7566   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7567 
7568   Not collective
7569 
7570   Input Parameters:
7571 + dm         - The `DM`
7572 . section    - The `PetscSection` describing the points (a local section)
7573 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7574 . point      - The point defining the closure
7575 - useClPerm  - Use the closure point permutation if available
7576 
7577   Output Parameters:
7578 + numIndices - The number of dof indices in the closure of point with the input sections
7579 . indices    - The dof indices
7580 . outOffsets - Array to write the field offsets into, or `NULL`
7581 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7582 
7583   Level: advanced
7584 
7585   Notes:
7586   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7587 
7588   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7589   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7590   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7591   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7592   indices (with the above semantics) are implied.
7593 
7594 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7595           `PetscSection`, `DMGetGlobalSection()`
7596 @*/
7597 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7598 {
7599   /* Closure ordering */
7600   PetscSection    clSection;
7601   IS              clPoints;
7602   const PetscInt *clp;
7603   PetscInt       *points;
7604   const PetscInt *clperm = NULL;
7605   /* Dof permutation and sign flips */
7606   const PetscInt    **perms[32] = {NULL};
7607   const PetscScalar **flips[32] = {NULL};
7608   PetscScalar        *valCopy   = NULL;
7609   /* Hanging node constraints */
7610   PetscInt    *pointsC = NULL;
7611   PetscScalar *valuesC = NULL;
7612   PetscInt     NclC, NiC;
7613 
7614   PetscInt *idx;
7615   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7616   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7617 
7618   PetscFunctionBeginHot;
7619   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7620   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7621   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7622   if (numIndices) PetscValidIntPointer(numIndices, 6);
7623   if (indices) PetscValidPointer(indices, 7);
7624   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7625   if (values) PetscValidPointer(values, 9);
7626   PetscCall(PetscSectionGetNumFields(section, &Nf));
7627   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7628   PetscCall(PetscArrayzero(offsets, 32));
7629   /* 1) Get points in closure */
7630   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7631   if (useClPerm) {
7632     PetscInt depth, clsize;
7633     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7634     for (clsize = 0, p = 0; p < Ncl; p++) {
7635       PetscInt dof;
7636       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7637       clsize += dof;
7638     }
7639     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7640   }
7641   /* 2) Get number of indices on these points and field offsets from section */
7642   for (p = 0; p < Ncl * 2; p += 2) {
7643     PetscInt dof, fdof;
7644 
7645     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7646     for (f = 0; f < Nf; ++f) {
7647       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7648       offsets[f + 1] += fdof;
7649     }
7650     Ni += dof;
7651   }
7652   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7653   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7654   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7655   for (f = 0; f < PetscMax(1, Nf); ++f) {
7656     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7657     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7658     /* may need to apply sign changes to the element matrix */
7659     if (values && flips[f]) {
7660       PetscInt foffset = offsets[f];
7661 
7662       for (p = 0; p < Ncl; ++p) {
7663         PetscInt           pnt  = points[2 * p], fdof;
7664         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7665 
7666         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7667         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7668         if (flip) {
7669           PetscInt i, j, k;
7670 
7671           if (!valCopy) {
7672             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7673             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7674             *values = valCopy;
7675           }
7676           for (i = 0; i < fdof; ++i) {
7677             PetscScalar fval = flip[i];
7678 
7679             for (k = 0; k < Ni; ++k) {
7680               valCopy[Ni * (foffset + i) + k] *= fval;
7681               valCopy[Ni * k + (foffset + i)] *= fval;
7682             }
7683           }
7684         }
7685         foffset += fdof;
7686       }
7687     }
7688   }
7689   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7690   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7691   if (NclC) {
7692     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7693     for (f = 0; f < PetscMax(1, Nf); ++f) {
7694       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7695       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7696     }
7697     for (f = 0; f < PetscMax(1, Nf); ++f) {
7698       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7699       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7700     }
7701     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7702     Ncl    = NclC;
7703     Ni     = NiC;
7704     points = pointsC;
7705     if (values) *values = valuesC;
7706   }
7707   /* 5) Calculate indices */
7708   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7709   if (Nf) {
7710     PetscInt  idxOff;
7711     PetscBool useFieldOffsets;
7712 
7713     if (outOffsets) {
7714       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7715     }
7716     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7717     if (useFieldOffsets) {
7718       for (p = 0; p < Ncl; ++p) {
7719         const PetscInt pnt = points[p * 2];
7720 
7721         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7722       }
7723     } else {
7724       for (p = 0; p < Ncl; ++p) {
7725         const PetscInt pnt = points[p * 2];
7726 
7727         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7728         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7729          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7730          * global section. */
7731         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7732       }
7733     }
7734   } else {
7735     PetscInt off = 0, idxOff;
7736 
7737     for (p = 0; p < Ncl; ++p) {
7738       const PetscInt  pnt  = points[p * 2];
7739       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7740 
7741       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7742       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7743        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7744       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7745     }
7746   }
7747   /* 6) Cleanup */
7748   for (f = 0; f < PetscMax(1, Nf); ++f) {
7749     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7750     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7751   }
7752   if (NclC) {
7753     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7754   } else {
7755     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7756   }
7757 
7758   if (numIndices) *numIndices = Ni;
7759   if (indices) *indices = idx;
7760   PetscFunctionReturn(PETSC_SUCCESS);
7761 }
7762 
7763 /*@C
7764   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7765 
7766   Not collective
7767 
7768   Input Parameters:
7769 + dm         - The `DM`
7770 . section    - The `PetscSection` describing the points (a local section)
7771 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7772 . point      - The point defining the closure
7773 - useClPerm  - Use the closure point permutation if available
7774 
7775   Output Parameters:
7776 + numIndices - The number of dof indices in the closure of point with the input sections
7777 . indices    - The dof indices
7778 . outOffsets - Array to write the field offsets into, or `NULL`
7779 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7780 
7781   Level: advanced
7782 
7783   Notes:
7784   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7785 
7786   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7787   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7788   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7789   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7790   indices (with the above semantics) are implied.
7791 
7792 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7793 @*/
7794 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7795 {
7796   PetscFunctionBegin;
7797   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7798   PetscValidPointer(indices, 7);
7799   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7800   PetscFunctionReturn(PETSC_SUCCESS);
7801 }
7802 
7803 /*@C
7804   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7805 
7806   Not collective
7807 
7808   Input Parameters:
7809 + dm - The `DM`
7810 . section - The section describing the layout in `v`, or `NULL` to use the default section
7811 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7812 . A - The matrix
7813 . point - The point in the `DM`
7814 . values - The array of values
7815 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7816 
7817   Level: intermediate
7818 
7819 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7820 @*/
7821 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7822 {
7823   DM_Plex           *mesh = (DM_Plex *)dm->data;
7824   PetscInt          *indices;
7825   PetscInt           numIndices;
7826   const PetscScalar *valuesOrig = values;
7827   PetscErrorCode     ierr;
7828 
7829   PetscFunctionBegin;
7830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7831   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7832   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7833   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7834   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7835   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7836 
7837   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7838 
7839   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7840   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7841   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7842   if (ierr) {
7843     PetscMPIInt rank;
7844 
7845     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7846     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7847     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7848     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7849     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7850     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7851   }
7852   if (mesh->printFEM > 1) {
7853     PetscInt i;
7854     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7855     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7856     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7857   }
7858 
7859   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7860   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7861   PetscFunctionReturn(PETSC_SUCCESS);
7862 }
7863 
7864 /*@C
7865   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7866 
7867   Not collective
7868 
7869   Input Parameters:
7870 + dmRow - The `DM` for the row fields
7871 . sectionRow - The section describing the layout, or `NULL` to use the default section in `dmRow`
7872 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7873 . dmCol - The `DM` for the column fields
7874 . sectionCol - The section describing the layout, or `NULL` to use the default section in `dmCol`
7875 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7876 . A - The matrix
7877 . point - The point in the `DM`
7878 . values - The array of values
7879 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7880 
7881   Level: intermediate
7882 
7883 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7884 @*/
7885 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7886 {
7887   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7888   PetscInt          *indicesRow, *indicesCol;
7889   PetscInt           numIndicesRow, numIndicesCol;
7890   const PetscScalar *valuesOrig = values;
7891   PetscErrorCode     ierr;
7892 
7893   PetscFunctionBegin;
7894   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7895   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7896   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7897   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7898   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7899   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7900   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7901   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7902   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7903   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7904   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7905 
7906   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7907   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7908 
7909   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7910   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7911   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7912   if (ierr) {
7913     PetscMPIInt rank;
7914 
7915     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7916     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7917     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7918     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7919     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7920     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7921   }
7922 
7923   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7924   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7925   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7926   PetscFunctionReturn(PETSC_SUCCESS);
7927 }
7928 
7929 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7930 {
7931   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7932   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7933   PetscInt       *cpoints = NULL;
7934   PetscInt       *findices, *cindices;
7935   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7936   PetscInt        foffsets[32], coffsets[32];
7937   DMPolytopeType  ct;
7938   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7939   PetscErrorCode  ierr;
7940 
7941   PetscFunctionBegin;
7942   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7943   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7944   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7945   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7946   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7947   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7948   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7949   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7950   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7951   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7952   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7953   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7954   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7955   PetscCall(PetscArrayzero(foffsets, 32));
7956   PetscCall(PetscArrayzero(coffsets, 32));
7957   /* Column indices */
7958   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7959   maxFPoints = numCPoints;
7960   /* Compress out points not in the section */
7961   /*   TODO: Squeeze out points with 0 dof as well */
7962   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7963   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7964     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7965       cpoints[q * 2]     = cpoints[p];
7966       cpoints[q * 2 + 1] = cpoints[p + 1];
7967       ++q;
7968     }
7969   }
7970   numCPoints = q;
7971   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7972     PetscInt fdof;
7973 
7974     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7975     if (!dof) continue;
7976     for (f = 0; f < numFields; ++f) {
7977       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7978       coffsets[f + 1] += fdof;
7979     }
7980     numCIndices += dof;
7981   }
7982   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7983   /* Row indices */
7984   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7985   {
7986     DMPlexTransform tr;
7987     DMPolytopeType *rct;
7988     PetscInt       *rsize, *rcone, *rornt, Nt;
7989 
7990     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7991     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7992     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7993     numSubcells = rsize[Nt - 1];
7994     PetscCall(DMPlexTransformDestroy(&tr));
7995   }
7996   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7997   for (r = 0, q = 0; r < numSubcells; ++r) {
7998     /* TODO Map from coarse to fine cells */
7999     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8000     /* Compress out points not in the section */
8001     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8002     for (p = 0; p < numFPoints * 2; p += 2) {
8003       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8004         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8005         if (!dof) continue;
8006         for (s = 0; s < q; ++s)
8007           if (fpoints[p] == ftotpoints[s * 2]) break;
8008         if (s < q) continue;
8009         ftotpoints[q * 2]     = fpoints[p];
8010         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8011         ++q;
8012       }
8013     }
8014     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8015   }
8016   numFPoints = q;
8017   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8018     PetscInt fdof;
8019 
8020     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8021     if (!dof) continue;
8022     for (f = 0; f < numFields; ++f) {
8023       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8024       foffsets[f + 1] += fdof;
8025     }
8026     numFIndices += dof;
8027   }
8028   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8029 
8030   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8031   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8032   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8033   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8034   if (numFields) {
8035     const PetscInt **permsF[32] = {NULL};
8036     const PetscInt **permsC[32] = {NULL};
8037 
8038     for (f = 0; f < numFields; f++) {
8039       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8040       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8041     }
8042     for (p = 0; p < numFPoints; p++) {
8043       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8044       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8045     }
8046     for (p = 0; p < numCPoints; p++) {
8047       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8048       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8049     }
8050     for (f = 0; f < numFields; f++) {
8051       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8052       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8053     }
8054   } else {
8055     const PetscInt **permsF = NULL;
8056     const PetscInt **permsC = NULL;
8057 
8058     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8059     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8060     for (p = 0, off = 0; p < numFPoints; p++) {
8061       const PetscInt *perm = permsF ? permsF[p] : NULL;
8062 
8063       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8064       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8065     }
8066     for (p = 0, off = 0; p < numCPoints; p++) {
8067       const PetscInt *perm = permsC ? permsC[p] : NULL;
8068 
8069       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8070       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8071     }
8072     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8073     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8074   }
8075   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8076   /* TODO: flips */
8077   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8078   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8079   if (ierr) {
8080     PetscMPIInt rank;
8081 
8082     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8083     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8084     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8085     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8086     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8087   }
8088   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8089   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8090   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8091   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8092   PetscFunctionReturn(PETSC_SUCCESS);
8093 }
8094 
8095 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8096 {
8097   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8098   PetscInt       *cpoints = NULL;
8099   PetscInt        foffsets[32], coffsets[32];
8100   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8101   DMPolytopeType  ct;
8102   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8103 
8104   PetscFunctionBegin;
8105   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8106   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8107   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8108   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8109   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8110   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8111   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8112   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8113   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8114   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8115   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8116   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8117   PetscCall(PetscArrayzero(foffsets, 32));
8118   PetscCall(PetscArrayzero(coffsets, 32));
8119   /* Column indices */
8120   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8121   maxFPoints = numCPoints;
8122   /* Compress out points not in the section */
8123   /*   TODO: Squeeze out points with 0 dof as well */
8124   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8125   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8126     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8127       cpoints[q * 2]     = cpoints[p];
8128       cpoints[q * 2 + 1] = cpoints[p + 1];
8129       ++q;
8130     }
8131   }
8132   numCPoints = q;
8133   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8134     PetscInt fdof;
8135 
8136     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8137     if (!dof) continue;
8138     for (f = 0; f < numFields; ++f) {
8139       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8140       coffsets[f + 1] += fdof;
8141     }
8142     numCIndices += dof;
8143   }
8144   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8145   /* Row indices */
8146   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8147   {
8148     DMPlexTransform tr;
8149     DMPolytopeType *rct;
8150     PetscInt       *rsize, *rcone, *rornt, Nt;
8151 
8152     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8153     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8154     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8155     numSubcells = rsize[Nt - 1];
8156     PetscCall(DMPlexTransformDestroy(&tr));
8157   }
8158   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8159   for (r = 0, q = 0; r < numSubcells; ++r) {
8160     /* TODO Map from coarse to fine cells */
8161     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8162     /* Compress out points not in the section */
8163     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8164     for (p = 0; p < numFPoints * 2; p += 2) {
8165       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8166         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8167         if (!dof) continue;
8168         for (s = 0; s < q; ++s)
8169           if (fpoints[p] == ftotpoints[s * 2]) break;
8170         if (s < q) continue;
8171         ftotpoints[q * 2]     = fpoints[p];
8172         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8173         ++q;
8174       }
8175     }
8176     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8177   }
8178   numFPoints = q;
8179   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8180     PetscInt fdof;
8181 
8182     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8183     if (!dof) continue;
8184     for (f = 0; f < numFields; ++f) {
8185       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8186       foffsets[f + 1] += fdof;
8187     }
8188     numFIndices += dof;
8189   }
8190   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8191 
8192   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8193   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8194   if (numFields) {
8195     const PetscInt **permsF[32] = {NULL};
8196     const PetscInt **permsC[32] = {NULL};
8197 
8198     for (f = 0; f < numFields; f++) {
8199       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8200       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8201     }
8202     for (p = 0; p < numFPoints; p++) {
8203       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8204       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8205     }
8206     for (p = 0; p < numCPoints; p++) {
8207       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8208       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8209     }
8210     for (f = 0; f < numFields; f++) {
8211       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8212       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8213     }
8214   } else {
8215     const PetscInt **permsF = NULL;
8216     const PetscInt **permsC = NULL;
8217 
8218     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8219     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8220     for (p = 0, off = 0; p < numFPoints; p++) {
8221       const PetscInt *perm = permsF ? permsF[p] : NULL;
8222 
8223       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8224       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8225     }
8226     for (p = 0, off = 0; p < numCPoints; p++) {
8227       const PetscInt *perm = permsC ? permsC[p] : NULL;
8228 
8229       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8230       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8231     }
8232     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8233     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8234   }
8235   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8236   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8237   PetscFunctionReturn(PETSC_SUCCESS);
8238 }
8239 
8240 /*@C
8241   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8242 
8243   Input Parameter:
8244 . dm   - The `DMPLEX` object
8245 
8246   Output Parameter:
8247 . cellHeight - The height of a cell
8248 
8249   Level: developer
8250 
8251 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8252 @*/
8253 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8254 {
8255   DM_Plex *mesh = (DM_Plex *)dm->data;
8256 
8257   PetscFunctionBegin;
8258   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8259   PetscValidIntPointer(cellHeight, 2);
8260   *cellHeight = mesh->vtkCellHeight;
8261   PetscFunctionReturn(PETSC_SUCCESS);
8262 }
8263 
8264 /*@C
8265   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8266 
8267   Input Parameters:
8268 + dm   - The `DMPLEX` object
8269 - cellHeight - The height of a cell
8270 
8271   Level: developer
8272 
8273 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8274 @*/
8275 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8276 {
8277   DM_Plex *mesh = (DM_Plex *)dm->data;
8278 
8279   PetscFunctionBegin;
8280   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8281   mesh->vtkCellHeight = cellHeight;
8282   PetscFunctionReturn(PETSC_SUCCESS);
8283 }
8284 
8285 /*@
8286   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8287 
8288   Input Parameter:
8289 . dm - The `DMPLEX` object
8290 
8291   Output Parameters:
8292 + gcStart - The first ghost cell, or `NULL`
8293 - gcEnd   - The upper bound on ghost cells, or `NULL`
8294 
8295   Level: advanced
8296 
8297 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8298 @*/
8299 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8300 {
8301   DMLabel ctLabel;
8302 
8303   PetscFunctionBegin;
8304   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8305   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8306   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8307   // Reset label for fast lookup
8308   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8309   PetscFunctionReturn(PETSC_SUCCESS);
8310 }
8311 
8312 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8313 {
8314   PetscSection section, globalSection;
8315   PetscInt    *numbers, p;
8316 
8317   PetscFunctionBegin;
8318   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8319   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8320   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8321   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8322   PetscCall(PetscSectionSetUp(section));
8323   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8324   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8325   for (p = pStart; p < pEnd; ++p) {
8326     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8327     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8328     else numbers[p - pStart] += shift;
8329   }
8330   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8331   if (globalSize) {
8332     PetscLayout layout;
8333     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8334     PetscCall(PetscLayoutGetSize(layout, globalSize));
8335     PetscCall(PetscLayoutDestroy(&layout));
8336   }
8337   PetscCall(PetscSectionDestroy(&section));
8338   PetscCall(PetscSectionDestroy(&globalSection));
8339   PetscFunctionReturn(PETSC_SUCCESS);
8340 }
8341 
8342 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8343 {
8344   PetscInt cellHeight, cStart, cEnd;
8345 
8346   PetscFunctionBegin;
8347   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8348   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8349   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8350   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8351   PetscFunctionReturn(PETSC_SUCCESS);
8352 }
8353 
8354 /*@
8355   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8356 
8357   Input Parameter:
8358 . dm   - The `DMPLEX` object
8359 
8360   Output Parameter:
8361 . globalCellNumbers - Global cell numbers for all cells on this process
8362 
8363   Level: developer
8364 
8365 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8366 @*/
8367 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8368 {
8369   DM_Plex *mesh = (DM_Plex *)dm->data;
8370 
8371   PetscFunctionBegin;
8372   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8373   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8374   *globalCellNumbers = mesh->globalCellNumbers;
8375   PetscFunctionReturn(PETSC_SUCCESS);
8376 }
8377 
8378 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8379 {
8380   PetscInt vStart, vEnd;
8381 
8382   PetscFunctionBegin;
8383   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8384   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8385   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8386   PetscFunctionReturn(PETSC_SUCCESS);
8387 }
8388 
8389 /*@
8390   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8391 
8392   Input Parameter:
8393 . dm   - The `DMPLEX` object
8394 
8395   Output Parameter:
8396 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8397 
8398   Level: developer
8399 
8400 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8401 @*/
8402 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8403 {
8404   DM_Plex *mesh = (DM_Plex *)dm->data;
8405 
8406   PetscFunctionBegin;
8407   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8408   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8409   *globalVertexNumbers = mesh->globalVertexNumbers;
8410   PetscFunctionReturn(PETSC_SUCCESS);
8411 }
8412 
8413 /*@
8414   DMPlexCreatePointNumbering - Create a global numbering for all points.
8415 
8416   Collective
8417 
8418   Input Parameter:
8419 . dm   - The `DMPLEX` object
8420 
8421   Output Parameter:
8422 . globalPointNumbers - Global numbers for all points on this process
8423 
8424   Level: developer
8425 
8426   Notes:
8427   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8428   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8429   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8430   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8431 
8432   The partitioned mesh is
8433 ```
8434  (2)--0--(3)--1--(4)    (1)--0--(2)
8435 ```
8436   and its global numbering is
8437 ```
8438   (3)--0--(4)--1--(5)--2--(6)
8439 ```
8440   Then the global numbering is provided as
8441 ```
8442 [0] Number of indices in set 5
8443 [0] 0 0
8444 [0] 1 1
8445 [0] 2 3
8446 [0] 3 4
8447 [0] 4 -6
8448 [1] Number of indices in set 3
8449 [1] 0 2
8450 [1] 1 5
8451 [1] 2 6
8452 ```
8453 
8454 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8455 @*/
8456 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8457 {
8458   IS        nums[4];
8459   PetscInt  depths[4], gdepths[4], starts[4];
8460   PetscInt  depth, d, shift = 0;
8461   PetscBool empty = PETSC_FALSE;
8462 
8463   PetscFunctionBegin;
8464   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8465   PetscCall(DMPlexGetDepth(dm, &depth));
8466   // For unstratified meshes use dim instead of depth
8467   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8468   // If any stratum is empty, we must mark all empty
8469   for (d = 0; d <= depth; ++d) {
8470     PetscInt end;
8471 
8472     depths[d] = depth - d;
8473     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8474     if (!(starts[d] - end)) empty = PETSC_TRUE;
8475   }
8476   if (empty)
8477     for (d = 0; d <= depth; ++d) {
8478       depths[d] = -1;
8479       starts[d] = -1;
8480     }
8481   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8482   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8483   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]);
8484   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8485   for (d = 0; d <= depth; ++d) {
8486     PetscInt pStart, pEnd, gsize;
8487 
8488     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8489     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8490     shift += gsize;
8491   }
8492   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8493   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8494   PetscFunctionReturn(PETSC_SUCCESS);
8495 }
8496 
8497 /*@
8498   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8499 
8500   Input Parameter:
8501 . dm - The `DMPLEX` object
8502 
8503   Output Parameter:
8504 . ranks - The rank field
8505 
8506   Options Database Key:
8507 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8508 
8509   Level: intermediate
8510 
8511 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8512 @*/
8513 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8514 {
8515   DM             rdm;
8516   PetscFE        fe;
8517   PetscScalar   *r;
8518   PetscMPIInt    rank;
8519   DMPolytopeType ct;
8520   PetscInt       dim, cStart, cEnd, c;
8521   PetscBool      simplex;
8522 
8523   PetscFunctionBeginUser;
8524   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8525   PetscValidPointer(ranks, 2);
8526   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8527   PetscCall(DMClone(dm, &rdm));
8528   PetscCall(DMGetDimension(rdm, &dim));
8529   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8530   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8531   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8532   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8533   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8534   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8535   PetscCall(PetscFEDestroy(&fe));
8536   PetscCall(DMCreateDS(rdm));
8537   PetscCall(DMCreateGlobalVector(rdm, ranks));
8538   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8539   PetscCall(VecGetArray(*ranks, &r));
8540   for (c = cStart; c < cEnd; ++c) {
8541     PetscScalar *lr;
8542 
8543     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8544     if (lr) *lr = rank;
8545   }
8546   PetscCall(VecRestoreArray(*ranks, &r));
8547   PetscCall(DMDestroy(&rdm));
8548   PetscFunctionReturn(PETSC_SUCCESS);
8549 }
8550 
8551 /*@
8552   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8553 
8554   Input Parameters:
8555 + dm    - The `DMPLEX`
8556 - label - The `DMLabel`
8557 
8558   Output Parameter:
8559 . val - The label value field
8560 
8561   Options Database Key:
8562 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8563 
8564   Level: intermediate
8565 
8566 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8567 @*/
8568 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8569 {
8570   DM           rdm;
8571   PetscFE      fe;
8572   PetscScalar *v;
8573   PetscInt     dim, cStart, cEnd, c;
8574 
8575   PetscFunctionBeginUser;
8576   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8577   PetscValidPointer(label, 2);
8578   PetscValidPointer(val, 3);
8579   PetscCall(DMClone(dm, &rdm));
8580   PetscCall(DMGetDimension(rdm, &dim));
8581   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8582   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8583   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8584   PetscCall(PetscFEDestroy(&fe));
8585   PetscCall(DMCreateDS(rdm));
8586   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8587   PetscCall(DMCreateGlobalVector(rdm, val));
8588   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8589   PetscCall(VecGetArray(*val, &v));
8590   for (c = cStart; c < cEnd; ++c) {
8591     PetscScalar *lv;
8592     PetscInt     cval;
8593 
8594     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8595     PetscCall(DMLabelGetValue(label, c, &cval));
8596     *lv = cval;
8597   }
8598   PetscCall(VecRestoreArray(*val, &v));
8599   PetscCall(DMDestroy(&rdm));
8600   PetscFunctionReturn(PETSC_SUCCESS);
8601 }
8602 
8603 /*@
8604   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8605 
8606   Input Parameter:
8607 . dm - The `DMPLEX` object
8608 
8609   Level: developer
8610 
8611   Notes:
8612   This is a useful diagnostic when creating meshes programmatically.
8613 
8614   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8615 
8616 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8617 @*/
8618 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8619 {
8620   PetscSection    coneSection, supportSection;
8621   const PetscInt *cone, *support;
8622   PetscInt        coneSize, c, supportSize, s;
8623   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8624   PetscBool       storagecheck = PETSC_TRUE;
8625 
8626   PetscFunctionBegin;
8627   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8628   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8629   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8630   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8631   /* Check that point p is found in the support of its cone points, and vice versa */
8632   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8633   for (p = pStart; p < pEnd; ++p) {
8634     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8635     PetscCall(DMPlexGetCone(dm, p, &cone));
8636     for (c = 0; c < coneSize; ++c) {
8637       PetscBool dup = PETSC_FALSE;
8638       PetscInt  d;
8639       for (d = c - 1; d >= 0; --d) {
8640         if (cone[c] == cone[d]) {
8641           dup = PETSC_TRUE;
8642           break;
8643         }
8644       }
8645       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8646       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8647       for (s = 0; s < supportSize; ++s) {
8648         if (support[s] == p) break;
8649       }
8650       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8651         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8652         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8653         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8654         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8655         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8656         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8657         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]);
8658         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8659       }
8660     }
8661     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8662     if (p != pp) {
8663       storagecheck = PETSC_FALSE;
8664       continue;
8665     }
8666     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8667     PetscCall(DMPlexGetSupport(dm, p, &support));
8668     for (s = 0; s < supportSize; ++s) {
8669       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8670       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8671       for (c = 0; c < coneSize; ++c) {
8672         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8673         if (cone[c] != pp) {
8674           c = 0;
8675           break;
8676         }
8677         if (cone[c] == p) break;
8678       }
8679       if (c >= coneSize) {
8680         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8681         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8682         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8683         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8684         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8685         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8686         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8687       }
8688     }
8689   }
8690   if (storagecheck) {
8691     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8692     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8693     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8694   }
8695   PetscFunctionReturn(PETSC_SUCCESS);
8696 }
8697 
8698 /*
8699   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.
8700 */
8701 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8702 {
8703   DMPolytopeType  cct;
8704   PetscInt        ptpoints[4];
8705   const PetscInt *cone, *ccone, *ptcone;
8706   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8707 
8708   PetscFunctionBegin;
8709   *unsplit = 0;
8710   switch (ct) {
8711   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8712     ptpoints[npt++] = c;
8713     break;
8714   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8715     PetscCall(DMPlexGetCone(dm, c, &cone));
8716     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8717     for (cp = 0; cp < coneSize; ++cp) {
8718       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8719       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8720     }
8721     break;
8722   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8723   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8724     PetscCall(DMPlexGetCone(dm, c, &cone));
8725     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8726     for (cp = 0; cp < coneSize; ++cp) {
8727       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8728       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8729       for (ccp = 0; ccp < cconeSize; ++ccp) {
8730         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8731         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8732           PetscInt p;
8733           for (p = 0; p < npt; ++p)
8734             if (ptpoints[p] == ccone[ccp]) break;
8735           if (p == npt) ptpoints[npt++] = ccone[ccp];
8736         }
8737       }
8738     }
8739     break;
8740   default:
8741     break;
8742   }
8743   for (pt = 0; pt < npt; ++pt) {
8744     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8745     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8746   }
8747   PetscFunctionReturn(PETSC_SUCCESS);
8748 }
8749 
8750 /*@
8751   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8752 
8753   Input Parameters:
8754 + dm - The `DMPLEX` object
8755 - cellHeight - Normally 0
8756 
8757   Level: developer
8758 
8759   Notes:
8760   This is a useful diagnostic when creating meshes programmatically.
8761   Currently applicable only to homogeneous simplex or tensor meshes.
8762 
8763   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8764 
8765 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8766 @*/
8767 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8768 {
8769   DMPlexInterpolatedFlag interp;
8770   DMPolytopeType         ct;
8771   PetscInt               vStart, vEnd, cStart, cEnd, c;
8772 
8773   PetscFunctionBegin;
8774   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8775   PetscCall(DMPlexIsInterpolated(dm, &interp));
8776   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8777   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8778   for (c = cStart; c < cEnd; ++c) {
8779     PetscInt *closure = NULL;
8780     PetscInt  coneSize, closureSize, cl, Nv = 0;
8781 
8782     PetscCall(DMPlexGetCellType(dm, c, &ct));
8783     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8784     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8785     if (interp == DMPLEX_INTERPOLATED_FULL) {
8786       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8787       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));
8788     }
8789     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8790     for (cl = 0; cl < closureSize * 2; cl += 2) {
8791       const PetscInt p = closure[cl];
8792       if ((p >= vStart) && (p < vEnd)) ++Nv;
8793     }
8794     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8795     /* Special Case: Tensor faces with identified vertices */
8796     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8797       PetscInt unsplit;
8798 
8799       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8800       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8801     }
8802     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));
8803   }
8804   PetscFunctionReturn(PETSC_SUCCESS);
8805 }
8806 
8807 /*@
8808   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8809 
8810   Collective
8811 
8812   Input Parameters:
8813 + dm - The `DMPLEX` object
8814 - cellHeight - Normally 0
8815 
8816   Level: developer
8817 
8818   Notes:
8819   This is a useful diagnostic when creating meshes programmatically.
8820   This routine is only relevant for meshes that are fully interpolated across all ranks.
8821   It will error out if a partially interpolated mesh is given on some rank.
8822   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8823 
8824   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8825 
8826 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8827 @*/
8828 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8829 {
8830   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8831   DMPlexInterpolatedFlag interpEnum;
8832 
8833   PetscFunctionBegin;
8834   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8835   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8836   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8837   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8838     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8839     PetscFunctionReturn(PETSC_SUCCESS);
8840   }
8841 
8842   PetscCall(DMGetDimension(dm, &dim));
8843   PetscCall(DMPlexGetDepth(dm, &depth));
8844   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8845   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8846     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8847     for (c = cStart; c < cEnd; ++c) {
8848       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8849       const DMPolytopeType *faceTypes;
8850       DMPolytopeType        ct;
8851       PetscInt              numFaces, coneSize, f;
8852       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8853 
8854       PetscCall(DMPlexGetCellType(dm, c, &ct));
8855       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8856       if (unsplit) continue;
8857       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8858       PetscCall(DMPlexGetCone(dm, c, &cone));
8859       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8860       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8861       for (cl = 0; cl < closureSize * 2; cl += 2) {
8862         const PetscInt p = closure[cl];
8863         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8864       }
8865       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8866       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);
8867       for (f = 0; f < numFaces; ++f) {
8868         DMPolytopeType fct;
8869         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8870 
8871         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8872         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8873         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8874           const PetscInt p = fclosure[cl];
8875           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8876         }
8877         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]);
8878         for (v = 0; v < fnumCorners; ++v) {
8879           if (fclosure[v] != faces[fOff + v]) {
8880             PetscInt v1;
8881 
8882             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8883             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8884             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8885             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8886             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8887             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]);
8888           }
8889         }
8890         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8891         fOff += faceSizes[f];
8892       }
8893       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8894       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8895     }
8896   }
8897   PetscFunctionReturn(PETSC_SUCCESS);
8898 }
8899 
8900 /*@
8901   DMPlexCheckGeometry - Check the geometry of mesh cells
8902 
8903   Input Parameter:
8904 . dm - The `DMPLEX` object
8905 
8906   Level: developer
8907 
8908   Notes:
8909   This is a useful diagnostic when creating meshes programmatically.
8910 
8911   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8912 
8913 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8914 @*/
8915 PetscErrorCode DMPlexCheckGeometry(DM dm)
8916 {
8917   Vec       coordinates;
8918   PetscReal detJ, J[9], refVol = 1.0;
8919   PetscReal vol;
8920   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8921 
8922   PetscFunctionBegin;
8923   PetscCall(DMGetDimension(dm, &dim));
8924   PetscCall(DMGetCoordinateDim(dm, &dE));
8925   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
8926   PetscCall(DMPlexGetDepth(dm, &depth));
8927   for (d = 0; d < dim; ++d) refVol *= 2.0;
8928   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8929   /* Make sure local coordinates are created, because that step is collective */
8930   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8931   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
8932   for (c = cStart; c < cEnd; ++c) {
8933     DMPolytopeType ct;
8934     PetscInt       unsplit;
8935     PetscBool      ignoreZeroVol = PETSC_FALSE;
8936 
8937     PetscCall(DMPlexGetCellType(dm, c, &ct));
8938     switch (ct) {
8939     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8940     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8941     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8942       ignoreZeroVol = PETSC_TRUE;
8943       break;
8944     default:
8945       break;
8946     }
8947     switch (ct) {
8948     case DM_POLYTOPE_TRI_PRISM:
8949     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8950     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8951     case DM_POLYTOPE_PYRAMID:
8952       continue;
8953     default:
8954       break;
8955     }
8956     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8957     if (unsplit) continue;
8958     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8959     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);
8960     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8961     /* This should work with periodicity since DG coordinates should be used */
8962     if (depth > 1) {
8963       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8964       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);
8965       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8966     }
8967   }
8968   PetscFunctionReturn(PETSC_SUCCESS);
8969 }
8970 
8971 /*@
8972   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
8973 
8974   Collective
8975 
8976   Input Parameters:
8977 + dm - The `DMPLEX` object
8978 . pointSF - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
8979 - allowExtraRoots - Flag to allow extra points not present in the `DM`
8980 
8981   Level: developer
8982 
8983   Notes:
8984   This is mainly intended for debugging/testing purposes.
8985 
8986   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8987 
8988   Extra roots can come from priodic cuts, where additional points appear on the boundary
8989 
8990 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
8991 @*/
8992 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
8993 {
8994   PetscInt           l, nleaves, nroots, overlap;
8995   const PetscInt    *locals;
8996   const PetscSFNode *remotes;
8997   PetscBool          distributed;
8998   MPI_Comm           comm;
8999   PetscMPIInt        rank;
9000 
9001   PetscFunctionBegin;
9002   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9003   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9004   else pointSF = dm->sf;
9005   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9006   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9007   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9008   {
9009     PetscMPIInt mpiFlag;
9010 
9011     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9012     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9013   }
9014   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9015   PetscCall(DMPlexIsDistributed(dm, &distributed));
9016   if (!distributed) {
9017     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);
9018     PetscFunctionReturn(PETSC_SUCCESS);
9019   }
9020   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);
9021   PetscCall(DMPlexGetOverlap(dm, &overlap));
9022 
9023   /* Check SF graph is compatible with DMPlex chart */
9024   {
9025     PetscInt pStart, pEnd, maxLeaf;
9026 
9027     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9028     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9029     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9030     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9031   }
9032 
9033   /* Check Point SF has no local points referenced */
9034   for (l = 0; l < nleaves; l++) {
9035     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);
9036   }
9037 
9038   /* Check there are no cells in interface */
9039   if (!overlap) {
9040     PetscInt cellHeight, cStart, cEnd;
9041 
9042     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9043     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9044     for (l = 0; l < nleaves; ++l) {
9045       const PetscInt point = locals ? locals[l] : l;
9046 
9047       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9048     }
9049   }
9050 
9051   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9052   {
9053     const PetscInt *rootdegree;
9054 
9055     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9056     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9057     for (l = 0; l < nleaves; ++l) {
9058       const PetscInt  point = locals ? locals[l] : l;
9059       const PetscInt *cone;
9060       PetscInt        coneSize, c, idx;
9061 
9062       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9063       PetscCall(DMPlexGetCone(dm, point, &cone));
9064       for (c = 0; c < coneSize; ++c) {
9065         if (!rootdegree[cone[c]]) {
9066           if (locals) {
9067             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9068           } else {
9069             idx = (cone[c] < nleaves) ? cone[c] : -1;
9070           }
9071           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9072         }
9073       }
9074     }
9075   }
9076   PetscFunctionReturn(PETSC_SUCCESS);
9077 }
9078 
9079 /*@
9080   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9081 
9082   Input Parameter:
9083 . dm - The `DMPLEX` object
9084 
9085   Level: developer
9086 
9087   Notes:
9088   This is a useful diagnostic when creating meshes programmatically.
9089 
9090   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9091 
9092   Currently does not include `DMPlexCheckCellShape()`.
9093 
9094 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9095 @*/
9096 PetscErrorCode DMPlexCheck(DM dm)
9097 {
9098   PetscInt cellHeight;
9099 
9100   PetscFunctionBegin;
9101   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9102   PetscCall(DMPlexCheckSymmetry(dm));
9103   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9104   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9105   PetscCall(DMPlexCheckGeometry(dm));
9106   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9107   PetscCall(DMPlexCheckInterfaceCones(dm));
9108   PetscFunctionReturn(PETSC_SUCCESS);
9109 }
9110 
9111 typedef struct cell_stats {
9112   PetscReal min, max, sum, squaresum;
9113   PetscInt  count;
9114 } cell_stats_t;
9115 
9116 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9117 {
9118   PetscInt i, N = *len;
9119 
9120   for (i = 0; i < N; i++) {
9121     cell_stats_t *A = (cell_stats_t *)a;
9122     cell_stats_t *B = (cell_stats_t *)b;
9123 
9124     B->min = PetscMin(A->min, B->min);
9125     B->max = PetscMax(A->max, B->max);
9126     B->sum += A->sum;
9127     B->squaresum += A->squaresum;
9128     B->count += A->count;
9129   }
9130 }
9131 
9132 /*@
9133   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9134 
9135   Collective
9136 
9137   Input Parameters:
9138 + dm        - The `DMPLEX` object
9139 . output    - If true, statistics will be displayed on `stdout`
9140 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9141 
9142   Level: developer
9143 
9144   Notes:
9145   This is mainly intended for debugging/testing purposes.
9146 
9147   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9148 
9149 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9150 @*/
9151 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9152 {
9153   DM           dmCoarse;
9154   cell_stats_t stats, globalStats;
9155   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9156   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9157   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9158   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9159   PetscMPIInt  rank, size;
9160 
9161   PetscFunctionBegin;
9162   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9163   stats.min = PETSC_MAX_REAL;
9164   stats.max = PETSC_MIN_REAL;
9165   stats.sum = stats.squaresum = 0.;
9166   stats.count                 = 0;
9167 
9168   PetscCallMPI(MPI_Comm_size(comm, &size));
9169   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9170   PetscCall(DMGetCoordinateDim(dm, &cdim));
9171   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9172   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9173   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9174   for (c = cStart; c < cEnd; c++) {
9175     PetscInt  i;
9176     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9177 
9178     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9179     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9180     for (i = 0; i < PetscSqr(cdim); ++i) {
9181       frobJ += J[i] * J[i];
9182       frobInvJ += invJ[i] * invJ[i];
9183     }
9184     cond2 = frobJ * frobInvJ;
9185     cond  = PetscSqrtReal(cond2);
9186 
9187     stats.min = PetscMin(stats.min, cond);
9188     stats.max = PetscMax(stats.max, cond);
9189     stats.sum += cond;
9190     stats.squaresum += cond2;
9191     stats.count++;
9192     if (output && cond > limit) {
9193       PetscSection coordSection;
9194       Vec          coordsLocal;
9195       PetscScalar *coords = NULL;
9196       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9197 
9198       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9199       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9200       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9201       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9202       for (i = 0; i < Nv / cdim; ++i) {
9203         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9204         for (d = 0; d < cdim; ++d) {
9205           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9206           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9207         }
9208         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9209       }
9210       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9211       for (cl = 0; cl < clSize * 2; cl += 2) {
9212         const PetscInt edge = closure[cl];
9213 
9214         if ((edge >= eStart) && (edge < eEnd)) {
9215           PetscReal len;
9216 
9217           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9218           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9219         }
9220       }
9221       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9222       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9223     }
9224   }
9225   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9226 
9227   if (size > 1) {
9228     PetscMPIInt  blockLengths[2] = {4, 1};
9229     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9230     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9231     MPI_Op       statReduce;
9232 
9233     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9234     PetscCallMPI(MPI_Type_commit(&statType));
9235     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9236     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9237     PetscCallMPI(MPI_Op_free(&statReduce));
9238     PetscCallMPI(MPI_Type_free(&statType));
9239   } else {
9240     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9241   }
9242   if (rank == 0) {
9243     count = globalStats.count;
9244     min   = globalStats.min;
9245     max   = globalStats.max;
9246     mean  = globalStats.sum / globalStats.count;
9247     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9248   }
9249 
9250   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));
9251   PetscCall(PetscFree2(J, invJ));
9252 
9253   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9254   if (dmCoarse) {
9255     PetscBool isplex;
9256 
9257     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9258     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9259   }
9260   PetscFunctionReturn(PETSC_SUCCESS);
9261 }
9262 
9263 /*@
9264   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9265   orthogonal quality below given tolerance.
9266 
9267   Collective
9268 
9269   Input Parameters:
9270 + dm   - The `DMPLEX` object
9271 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9272 - atol - [0, 1] Absolute tolerance for tagging cells.
9273 
9274   Output Parameters:
9275 + OrthQual      - `Vec` containing orthogonal quality per cell
9276 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9277 
9278   Options Database Keys:
9279 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9280 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9281 
9282   Level: intermediate
9283 
9284   Notes:
9285   Orthogonal quality is given by the following formula:
9286 
9287   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9288 
9289   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
9290   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9291   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9292   calculating the cosine of the angle between these vectors.
9293 
9294   Orthogonal quality ranges from 1 (best) to 0 (worst).
9295 
9296   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9297   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9298 
9299   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9300 
9301 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9302 @*/
9303 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9304 {
9305   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9306   PetscInt              *idx;
9307   PetscScalar           *oqVals;
9308   const PetscScalar     *cellGeomArr, *faceGeomArr;
9309   PetscReal             *ci, *fi, *Ai;
9310   MPI_Comm               comm;
9311   Vec                    cellgeom, facegeom;
9312   DM                     dmFace, dmCell;
9313   IS                     glob;
9314   ISLocalToGlobalMapping ltog;
9315   PetscViewer            vwr;
9316 
9317   PetscFunctionBegin;
9318   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9319   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9320   PetscValidPointer(OrthQual, 4);
9321   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9322   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9323   PetscCall(DMGetDimension(dm, &nc));
9324   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9325   {
9326     DMPlexInterpolatedFlag interpFlag;
9327 
9328     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9329     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9330       PetscMPIInt rank;
9331 
9332       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9333       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9334     }
9335   }
9336   if (OrthQualLabel) {
9337     PetscValidPointer(OrthQualLabel, 5);
9338     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9339     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9340   } else {
9341     *OrthQualLabel = NULL;
9342   }
9343   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9344   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9345   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9346   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9347   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9348   PetscCall(VecCreate(comm, OrthQual));
9349   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9350   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9351   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9352   PetscCall(VecSetUp(*OrthQual));
9353   PetscCall(ISDestroy(&glob));
9354   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9355   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9356   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9357   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9358   PetscCall(VecGetDM(cellgeom, &dmCell));
9359   PetscCall(VecGetDM(facegeom, &dmFace));
9360   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9361   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9362     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9363     PetscInt         cellarr[2], *adj = NULL;
9364     PetscScalar     *cArr, *fArr;
9365     PetscReal        minvalc = 1.0, minvalf = 1.0;
9366     PetscFVCellGeom *cg;
9367 
9368     idx[cellIter] = cell - cStart;
9369     cellarr[0]    = cell;
9370     /* Make indexing into cellGeom easier */
9371     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9372     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9373     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9374     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9375     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9376       PetscInt         i;
9377       const PetscInt   neigh  = adj[cellneigh];
9378       PetscReal        normci = 0, normfi = 0, normai = 0;
9379       PetscFVCellGeom *cgneigh;
9380       PetscFVFaceGeom *fg;
9381 
9382       /* Don't count ourselves in the neighbor list */
9383       if (neigh == cell) continue;
9384       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9385       cellarr[1] = neigh;
9386       {
9387         PetscInt        numcovpts;
9388         const PetscInt *covpts;
9389 
9390         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9391         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9392         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9393       }
9394 
9395       /* Compute c_i, f_i and their norms */
9396       for (i = 0; i < nc; i++) {
9397         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9398         fi[i] = fg->centroid[i] - cg->centroid[i];
9399         Ai[i] = fg->normal[i];
9400         normci += PetscPowReal(ci[i], 2);
9401         normfi += PetscPowReal(fi[i], 2);
9402         normai += PetscPowReal(Ai[i], 2);
9403       }
9404       normci = PetscSqrtReal(normci);
9405       normfi = PetscSqrtReal(normfi);
9406       normai = PetscSqrtReal(normai);
9407 
9408       /* Normalize and compute for each face-cell-normal pair */
9409       for (i = 0; i < nc; i++) {
9410         ci[i] = ci[i] / normci;
9411         fi[i] = fi[i] / normfi;
9412         Ai[i] = Ai[i] / normai;
9413         /* PetscAbs because I don't know if normals are guaranteed to point out */
9414         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9415         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9416       }
9417       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9418       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9419     }
9420     PetscCall(PetscFree(adj));
9421     PetscCall(PetscFree2(cArr, fArr));
9422     /* Defer to cell if they're equal */
9423     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9424     if (OrthQualLabel) {
9425       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9426     }
9427   }
9428   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9429   PetscCall(VecAssemblyBegin(*OrthQual));
9430   PetscCall(VecAssemblyEnd(*OrthQual));
9431   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9432   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9433   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9434   if (OrthQualLabel) {
9435     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9436   }
9437   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9438   PetscCall(PetscViewerDestroy(&vwr));
9439   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9440   PetscFunctionReturn(PETSC_SUCCESS);
9441 }
9442 
9443 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9444  * interpolator construction */
9445 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9446 {
9447   PetscSection section, newSection, gsection;
9448   PetscSF      sf;
9449   PetscBool    hasConstraints, ghasConstraints;
9450 
9451   PetscFunctionBegin;
9452   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9453   PetscValidPointer(odm, 2);
9454   PetscCall(DMGetLocalSection(dm, &section));
9455   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9456   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9457   if (!ghasConstraints) {
9458     PetscCall(PetscObjectReference((PetscObject)dm));
9459     *odm = dm;
9460     PetscFunctionReturn(PETSC_SUCCESS);
9461   }
9462   PetscCall(DMClone(dm, odm));
9463   PetscCall(DMCopyFields(dm, *odm));
9464   PetscCall(DMGetLocalSection(*odm, &newSection));
9465   PetscCall(DMGetPointSF(*odm, &sf));
9466   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9467   PetscCall(DMSetGlobalSection(*odm, gsection));
9468   PetscCall(PetscSectionDestroy(&gsection));
9469   PetscFunctionReturn(PETSC_SUCCESS);
9470 }
9471 
9472 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9473 {
9474   DM        dmco, dmfo;
9475   Mat       interpo;
9476   Vec       rscale;
9477   Vec       cglobalo, clocal;
9478   Vec       fglobal, fglobalo, flocal;
9479   PetscBool regular;
9480 
9481   PetscFunctionBegin;
9482   PetscCall(DMGetFullDM(dmc, &dmco));
9483   PetscCall(DMGetFullDM(dmf, &dmfo));
9484   PetscCall(DMSetCoarseDM(dmfo, dmco));
9485   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9486   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9487   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9488   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9489   PetscCall(DMCreateLocalVector(dmc, &clocal));
9490   PetscCall(VecSet(cglobalo, 0.));
9491   PetscCall(VecSet(clocal, 0.));
9492   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9493   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9494   PetscCall(DMCreateLocalVector(dmf, &flocal));
9495   PetscCall(VecSet(fglobal, 0.));
9496   PetscCall(VecSet(fglobalo, 0.));
9497   PetscCall(VecSet(flocal, 0.));
9498   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9499   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9500   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9501   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9502   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9503   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9504   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9505   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9506   *shift = fglobal;
9507   PetscCall(VecDestroy(&flocal));
9508   PetscCall(VecDestroy(&fglobalo));
9509   PetscCall(VecDestroy(&clocal));
9510   PetscCall(VecDestroy(&cglobalo));
9511   PetscCall(VecDestroy(&rscale));
9512   PetscCall(MatDestroy(&interpo));
9513   PetscCall(DMDestroy(&dmfo));
9514   PetscCall(DMDestroy(&dmco));
9515   PetscFunctionReturn(PETSC_SUCCESS);
9516 }
9517 
9518 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9519 {
9520   PetscObject shifto;
9521   Vec         shift;
9522 
9523   PetscFunctionBegin;
9524   if (!interp) {
9525     Vec rscale;
9526 
9527     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9528     PetscCall(VecDestroy(&rscale));
9529   } else {
9530     PetscCall(PetscObjectReference((PetscObject)interp));
9531   }
9532   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9533   if (!shifto) {
9534     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9535     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9536     shifto = (PetscObject)shift;
9537     PetscCall(VecDestroy(&shift));
9538   }
9539   shift = (Vec)shifto;
9540   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9541   PetscCall(VecAXPY(fineSol, 1.0, shift));
9542   PetscCall(MatDestroy(&interp));
9543   PetscFunctionReturn(PETSC_SUCCESS);
9544 }
9545 
9546 /* Pointwise interpolation
9547      Just code FEM for now
9548      u^f = I u^c
9549      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9550      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9551      I_{ij} = psi^f_i phi^c_j
9552 */
9553 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9554 {
9555   PetscSection gsc, gsf;
9556   PetscInt     m, n;
9557   void        *ctx;
9558   DM           cdm;
9559   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9560 
9561   PetscFunctionBegin;
9562   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9563   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9564   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9565   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9566 
9567   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9568   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9569   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9570   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9571   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9572 
9573   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9574   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9575   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9576   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9577   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9578   if (scaling) {
9579     /* Use naive scaling */
9580     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9581   }
9582   PetscFunctionReturn(PETSC_SUCCESS);
9583 }
9584 
9585 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9586 {
9587   VecScatter ctx;
9588 
9589   PetscFunctionBegin;
9590   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9591   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9592   PetscCall(VecScatterDestroy(&ctx));
9593   PetscFunctionReturn(PETSC_SUCCESS);
9594 }
9595 
9596 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[])
9597 {
9598   const PetscInt Nc = uOff[1] - uOff[0];
9599   PetscInt       c;
9600   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9601 }
9602 
9603 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9604 {
9605   DM           dmc;
9606   PetscDS      ds;
9607   Vec          ones, locmass;
9608   IS           cellIS;
9609   PetscFormKey key;
9610   PetscInt     depth;
9611 
9612   PetscFunctionBegin;
9613   PetscCall(DMClone(dm, &dmc));
9614   PetscCall(DMCopyDisc(dm, dmc));
9615   PetscCall(DMGetDS(dmc, &ds));
9616   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9617   PetscCall(DMCreateGlobalVector(dmc, mass));
9618   PetscCall(DMGetLocalVector(dmc, &ones));
9619   PetscCall(DMGetLocalVector(dmc, &locmass));
9620   PetscCall(DMPlexGetDepth(dmc, &depth));
9621   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9622   PetscCall(VecSet(locmass, 0.0));
9623   PetscCall(VecSet(ones, 1.0));
9624   key.label = NULL;
9625   key.value = 0;
9626   key.field = 0;
9627   key.part  = 0;
9628   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9629   PetscCall(ISDestroy(&cellIS));
9630   PetscCall(VecSet(*mass, 0.0));
9631   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9632   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9633   PetscCall(DMRestoreLocalVector(dmc, &ones));
9634   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9635   PetscCall(DMDestroy(&dmc));
9636   PetscFunctionReturn(PETSC_SUCCESS);
9637 }
9638 
9639 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9640 {
9641   PetscSection gsc, gsf;
9642   PetscInt     m, n;
9643   void        *ctx;
9644   DM           cdm;
9645   PetscBool    regular;
9646 
9647   PetscFunctionBegin;
9648   if (dmFine == dmCoarse) {
9649     DM            dmc;
9650     PetscDS       ds;
9651     PetscWeakForm wf;
9652     Vec           u;
9653     IS            cellIS;
9654     PetscFormKey  key;
9655     PetscInt      depth;
9656 
9657     PetscCall(DMClone(dmFine, &dmc));
9658     PetscCall(DMCopyDisc(dmFine, dmc));
9659     PetscCall(DMGetDS(dmc, &ds));
9660     PetscCall(PetscDSGetWeakForm(ds, &wf));
9661     PetscCall(PetscWeakFormClear(wf));
9662     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9663     PetscCall(DMCreateMatrix(dmc, mass));
9664     PetscCall(DMGetLocalVector(dmc, &u));
9665     PetscCall(DMPlexGetDepth(dmc, &depth));
9666     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9667     PetscCall(MatZeroEntries(*mass));
9668     key.label = NULL;
9669     key.value = 0;
9670     key.field = 0;
9671     key.part  = 0;
9672     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9673     PetscCall(ISDestroy(&cellIS));
9674     PetscCall(DMRestoreLocalVector(dmc, &u));
9675     PetscCall(DMDestroy(&dmc));
9676   } else {
9677     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9678     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9679     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9680     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9681 
9682     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9683     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9684     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9685     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9686 
9687     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9688     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9689     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9690     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9691   }
9692   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9693   PetscFunctionReturn(PETSC_SUCCESS);
9694 }
9695 
9696 /*@
9697   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9698 
9699   Input Parameter:
9700 . dm - The `DMPLEX` object
9701 
9702   Output Parameter:
9703 . regular - The flag
9704 
9705   Level: intermediate
9706 
9707 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9708 @*/
9709 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9710 {
9711   PetscFunctionBegin;
9712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9713   PetscValidBoolPointer(regular, 2);
9714   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9715   PetscFunctionReturn(PETSC_SUCCESS);
9716 }
9717 
9718 /*@
9719   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9720 
9721   Input Parameters:
9722 + dm - The `DMPLEX` object
9723 - regular - The flag
9724 
9725   Level: intermediate
9726 
9727 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9728 @*/
9729 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9730 {
9731   PetscFunctionBegin;
9732   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9733   ((DM_Plex *)dm->data)->regularRefinement = regular;
9734   PetscFunctionReturn(PETSC_SUCCESS);
9735 }
9736 
9737 /*@
9738   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9739   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9740 
9741   Not Collective
9742 
9743   Input Parameter:
9744 . dm - The `DMPLEX` object
9745 
9746   Output Parameters:
9747 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9748 - anchorIS - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9749 
9750   Level: intermediate
9751 
9752 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9753 @*/
9754 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9755 {
9756   DM_Plex *plex = (DM_Plex *)dm->data;
9757 
9758   PetscFunctionBegin;
9759   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9760   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9761   if (anchorSection) *anchorSection = plex->anchorSection;
9762   if (anchorIS) *anchorIS = plex->anchorIS;
9763   PetscFunctionReturn(PETSC_SUCCESS);
9764 }
9765 
9766 /*@
9767   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9768   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9769   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9770 
9771   Collective
9772 
9773   Input Parameters:
9774 + dm - The `DMPLEX` object
9775 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9776                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9777 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9778 
9779   Level: intermediate
9780 
9781   Notes:
9782   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9783   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9784 
9785   The reference counts of `anchorSection` and `anchorIS` are incremented.
9786 
9787 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9788 @*/
9789 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9790 {
9791   DM_Plex    *plex = (DM_Plex *)dm->data;
9792   PetscMPIInt result;
9793 
9794   PetscFunctionBegin;
9795   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9796   if (anchorSection) {
9797     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9798     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9799     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9800   }
9801   if (anchorIS) {
9802     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9803     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9804     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9805   }
9806 
9807   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9808   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9809   plex->anchorSection = anchorSection;
9810 
9811   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9812   PetscCall(ISDestroy(&plex->anchorIS));
9813   plex->anchorIS = anchorIS;
9814 
9815   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9816     PetscInt        size, a, pStart, pEnd;
9817     const PetscInt *anchors;
9818 
9819     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9820     PetscCall(ISGetLocalSize(anchorIS, &size));
9821     PetscCall(ISGetIndices(anchorIS, &anchors));
9822     for (a = 0; a < size; a++) {
9823       PetscInt p;
9824 
9825       p = anchors[a];
9826       if (p >= pStart && p < pEnd) {
9827         PetscInt dof;
9828 
9829         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9830         if (dof) {
9831           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9832           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9833         }
9834       }
9835     }
9836     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9837   }
9838   /* reset the generic constraints */
9839   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9840   PetscFunctionReturn(PETSC_SUCCESS);
9841 }
9842 
9843 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9844 {
9845   PetscSection anchorSection;
9846   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9847 
9848   PetscFunctionBegin;
9849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9850   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9851   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9852   PetscCall(PetscSectionGetNumFields(section, &numFields));
9853   if (numFields) {
9854     PetscInt f;
9855     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9856 
9857     for (f = 0; f < numFields; f++) {
9858       PetscInt numComp;
9859 
9860       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9861       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9862     }
9863   }
9864   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9865   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9866   pStart = PetscMax(pStart, sStart);
9867   pEnd   = PetscMin(pEnd, sEnd);
9868   pEnd   = PetscMax(pStart, pEnd);
9869   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9870   for (p = pStart; p < pEnd; p++) {
9871     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9872     if (dof) {
9873       PetscCall(PetscSectionGetDof(section, p, &dof));
9874       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9875       for (f = 0; f < numFields; f++) {
9876         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9877         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9878       }
9879     }
9880   }
9881   PetscCall(PetscSectionSetUp(*cSec));
9882   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9883   PetscFunctionReturn(PETSC_SUCCESS);
9884 }
9885 
9886 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9887 {
9888   PetscSection    aSec;
9889   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9890   const PetscInt *anchors;
9891   PetscInt        numFields, f;
9892   IS              aIS;
9893   MatType         mtype;
9894   PetscBool       iscuda, iskokkos;
9895 
9896   PetscFunctionBegin;
9897   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9898   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9899   PetscCall(PetscSectionGetStorageSize(section, &n));
9900   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9901   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9902   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9903   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9904   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9905   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9906   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9907   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9908   else mtype = MATSEQAIJ;
9909   PetscCall(MatSetType(*cMat, mtype));
9910   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9911   PetscCall(ISGetIndices(aIS, &anchors));
9912   /* cSec will be a subset of aSec and section */
9913   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9914   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9915   PetscCall(PetscMalloc1(m + 1, &i));
9916   i[0] = 0;
9917   PetscCall(PetscSectionGetNumFields(section, &numFields));
9918   for (p = pStart; p < pEnd; p++) {
9919     PetscInt rDof, rOff, r;
9920 
9921     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9922     if (!rDof) continue;
9923     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9924     if (numFields) {
9925       for (f = 0; f < numFields; f++) {
9926         annz = 0;
9927         for (r = 0; r < rDof; r++) {
9928           a = anchors[rOff + r];
9929           if (a < sStart || a >= sEnd) continue;
9930           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9931           annz += aDof;
9932         }
9933         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9934         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9935         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9936       }
9937     } else {
9938       annz = 0;
9939       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9940       for (q = 0; q < dof; q++) {
9941         a = anchors[rOff + q];
9942         if (a < sStart || a >= sEnd) continue;
9943         PetscCall(PetscSectionGetDof(section, a, &aDof));
9944         annz += aDof;
9945       }
9946       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9947       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9948       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9949     }
9950   }
9951   nnz = i[m];
9952   PetscCall(PetscMalloc1(nnz, &j));
9953   offset = 0;
9954   for (p = pStart; p < pEnd; p++) {
9955     if (numFields) {
9956       for (f = 0; f < numFields; f++) {
9957         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9958         for (q = 0; q < dof; q++) {
9959           PetscInt rDof, rOff, r;
9960           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9961           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9962           for (r = 0; r < rDof; r++) {
9963             PetscInt s;
9964 
9965             a = anchors[rOff + r];
9966             if (a < sStart || a >= sEnd) continue;
9967             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9968             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9969             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9970           }
9971         }
9972       }
9973     } else {
9974       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9975       for (q = 0; q < dof; q++) {
9976         PetscInt rDof, rOff, r;
9977         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9978         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9979         for (r = 0; r < rDof; r++) {
9980           PetscInt s;
9981 
9982           a = anchors[rOff + r];
9983           if (a < sStart || a >= sEnd) continue;
9984           PetscCall(PetscSectionGetDof(section, a, &aDof));
9985           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9986           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9987         }
9988       }
9989     }
9990   }
9991   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9992   PetscCall(PetscFree(i));
9993   PetscCall(PetscFree(j));
9994   PetscCall(ISRestoreIndices(aIS, &anchors));
9995   PetscFunctionReturn(PETSC_SUCCESS);
9996 }
9997 
9998 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9999 {
10000   DM_Plex     *plex = (DM_Plex *)dm->data;
10001   PetscSection anchorSection, section, cSec;
10002   Mat          cMat;
10003 
10004   PetscFunctionBegin;
10005   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10006   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10007   if (anchorSection) {
10008     PetscInt Nf;
10009 
10010     PetscCall(DMGetLocalSection(dm, &section));
10011     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10012     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10013     PetscCall(DMGetNumFields(dm, &Nf));
10014     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10015     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10016     PetscCall(PetscSectionDestroy(&cSec));
10017     PetscCall(MatDestroy(&cMat));
10018   }
10019   PetscFunctionReturn(PETSC_SUCCESS);
10020 }
10021 
10022 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10023 {
10024   IS           subis;
10025   PetscSection section, subsection;
10026 
10027   PetscFunctionBegin;
10028   PetscCall(DMGetLocalSection(dm, &section));
10029   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10030   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10031   /* Create subdomain */
10032   PetscCall(DMPlexFilter(dm, label, value, subdm));
10033   /* Create submodel */
10034   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10035   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10036   PetscCall(DMSetLocalSection(*subdm, subsection));
10037   PetscCall(PetscSectionDestroy(&subsection));
10038   PetscCall(DMCopyDisc(dm, *subdm));
10039   /* Create map from submodel to global model */
10040   if (is) {
10041     PetscSection    sectionGlobal, subsectionGlobal;
10042     IS              spIS;
10043     const PetscInt *spmap;
10044     PetscInt       *subIndices;
10045     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10046     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10047 
10048     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10049     PetscCall(ISGetIndices(spIS, &spmap));
10050     PetscCall(PetscSectionGetNumFields(section, &Nf));
10051     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10052     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10053     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10054     for (p = pStart; p < pEnd; ++p) {
10055       PetscInt gdof, pSubSize = 0;
10056 
10057       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10058       if (gdof > 0) {
10059         for (f = 0; f < Nf; ++f) {
10060           PetscInt fdof, fcdof;
10061 
10062           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10063           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10064           pSubSize += fdof - fcdof;
10065         }
10066         subSize += pSubSize;
10067         if (pSubSize) {
10068           if (bs < 0) {
10069             bs = pSubSize;
10070           } else if (bs != pSubSize) {
10071             /* Layout does not admit a pointwise block size */
10072             bs = 1;
10073           }
10074         }
10075       }
10076     }
10077     /* Must have same blocksize on all procs (some might have no points) */
10078     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10079     bsLocal[1] = bs;
10080     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10081     if (bsMinMax[0] != bsMinMax[1]) {
10082       bs = 1;
10083     } else {
10084       bs = bsMinMax[0];
10085     }
10086     PetscCall(PetscMalloc1(subSize, &subIndices));
10087     for (p = pStart; p < pEnd; ++p) {
10088       PetscInt gdof, goff;
10089 
10090       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10091       if (gdof > 0) {
10092         const PetscInt point = spmap[p];
10093 
10094         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10095         for (f = 0; f < Nf; ++f) {
10096           PetscInt fdof, fcdof, fc, f2, poff = 0;
10097 
10098           /* Can get rid of this loop by storing field information in the global section */
10099           for (f2 = 0; f2 < f; ++f2) {
10100             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10101             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10102             poff += fdof - fcdof;
10103           }
10104           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10105           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10106           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10107         }
10108       }
10109     }
10110     PetscCall(ISRestoreIndices(spIS, &spmap));
10111     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10112     if (bs > 1) {
10113       /* We need to check that the block size does not come from non-contiguous fields */
10114       PetscInt i, j, set = 1;
10115       for (i = 0; i < subSize; i += bs) {
10116         for (j = 0; j < bs; ++j) {
10117           if (subIndices[i + j] != subIndices[i] + j) {
10118             set = 0;
10119             break;
10120           }
10121         }
10122       }
10123       if (set) PetscCall(ISSetBlockSize(*is, bs));
10124     }
10125     /* Attach nullspace */
10126     for (f = 0; f < Nf; ++f) {
10127       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10128       if ((*subdm)->nullspaceConstructors[f]) break;
10129     }
10130     if (f < Nf) {
10131       MatNullSpace nullSpace;
10132       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10133 
10134       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10135       PetscCall(MatNullSpaceDestroy(&nullSpace));
10136     }
10137   }
10138   PetscFunctionReturn(PETSC_SUCCESS);
10139 }
10140 
10141 /*@
10142   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10143 
10144   Input Parameters:
10145 + dm - The `DM`
10146 - dummy - unused argument
10147 
10148   Options Database Key:
10149 . -dm_plex_monitor_throughput - Activate the monitor
10150 
10151   Level: developer
10152 
10153 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10154 @*/
10155 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10156 {
10157 #if defined(PETSC_USE_LOG)
10158   PetscStageLog      stageLog;
10159   PetscLogEvent      event;
10160   PetscLogStage      stage;
10161   PetscEventPerfInfo eventInfo;
10162   PetscReal          cellRate, flopRate;
10163   PetscInt           cStart, cEnd, Nf, N;
10164   const char        *name;
10165 #endif
10166 
10167   PetscFunctionBegin;
10168   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10169 #if defined(PETSC_USE_LOG)
10170   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10171   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10172   PetscCall(DMGetNumFields(dm, &Nf));
10173   PetscCall(PetscLogGetStageLog(&stageLog));
10174   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10175   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10176   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10177   N        = (cEnd - cStart) * Nf * eventInfo.count;
10178   flopRate = eventInfo.flops / eventInfo.time;
10179   cellRate = N / eventInfo.time;
10180   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)));
10181 #else
10182   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10183 #endif
10184   PetscFunctionReturn(PETSC_SUCCESS);
10185 }
10186