xref: /petsc/src/dm/impls/plex/plex.c (revision 4e2e9504eff61fd362b9eabd44dfe3883745e908)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The `DMPLEX` object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(0);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(0);
49 }
50 
51 /*@
52   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
53 
54   Input Parameters:
55 + dm     - The `DMPLEX` object
56 - height - The cell height in the Plex, 0 is the default
57 
58   Output Parameters:
59 + cStart - The first "normal" cell
60 - cEnd   - The upper bound on "normal"" cells
61 
62   Level: developer
63 
64   Note:
65   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
66 
67 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(0);
106 }
107 
108 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
109 {
110   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
111   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
112 
113   PetscFunctionBegin;
114   *ft = PETSC_VTK_INVALID;
115   PetscCall(DMGetCoordinateDim(dm, &cdim));
116   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
118   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
119   if (field >= 0) {
120     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
121     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
122   } else {
123     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
124     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
125   }
126   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(0);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective on dm
154 
155   Input Parameters:
156 + dm - The `DMPLEX` object
157 . n  - The number of vectors
158 . u  - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(0);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrcpy(tmpname, vname));
202         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
203         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(0);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(0);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *coords, *array;
253   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(VecGetArrayRead(coordinates, &coords));
278   for (c = 0; c < N; c += dim) {
279     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
280     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
281     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
282     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
283   }
284   PetscCall(VecRestoreArrayRead(coordinates, &coords));
285   PetscCall(PetscDrawClear(draw));
286 
287   /* Could implement something like DMDASelectFields() */
288   for (f = 0; f < Nf; ++f) {
289     DM          fdm = dm;
290     Vec         fv  = v;
291     IS          fis;
292     char        prefix[PETSC_MAX_PATH_LEN];
293     const char *fname;
294 
295     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
296     PetscCall(PetscSectionGetFieldName(s, f, &fname));
297 
298     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
299     else prefix[0] = '\0';
300     if (Nf > 1) {
301       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
302       PetscCall(VecGetSubVector(v, fis, &fv));
303       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
304       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
305     }
306     for (comp = 0; comp < Nc; ++comp, ++w) {
307       PetscInt nmax = 2;
308 
309       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
310       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
311       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
312       PetscCall(PetscDrawSetTitle(draw, title));
313 
314       /* TODO Get max and min only for this component */
315       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
316       if (!flg) {
317         PetscCall(VecMin(fv, NULL, &vbound[0]));
318         PetscCall(VecMax(fv, NULL, &vbound[1]));
319         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
320       }
321       PetscCall(PetscDrawGetPopup(draw, &popup));
322       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
323       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
324 
325       PetscCall(VecGetArrayRead(fv, &array));
326       for (c = cStart; c < cEnd; ++c) {
327         PetscScalar *coords = NULL, *a   = NULL;
328         PetscInt     numCoords, color[4] = {-1, -1, -1, -1};
329 
330         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
331         if (a) {
332           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
333           color[1] = color[2] = color[3] = color[0];
334         } else {
335           PetscScalar *vals = NULL;
336           PetscInt     numVals, va;
337 
338           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
339           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
340           switch (numVals / Nc) {
341           case 3: /* P1 Triangle */
342           case 4: /* P1 Quadrangle */
343             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
344             break;
345           case 6: /* P2 Triangle */
346           case 8: /* P2 Quadrangle */
347             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
348             break;
349           default:
350             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
351           }
352           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
353         }
354         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
355         switch (numCoords) {
356         case 6:
357         case 12: /* Localized triangle */
358           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
359           break;
360         case 8:
361         case 16: /* Localized quadrilateral */
362           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
363           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
364           break;
365         default:
366           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
367         }
368         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
369       }
370       PetscCall(VecRestoreArrayRead(fv, &array));
371       PetscCall(PetscDrawFlush(draw));
372       PetscCall(PetscDrawPause(draw));
373       PetscCall(PetscDrawSave(draw));
374     }
375     if (Nf > 1) {
376       PetscCall(VecRestoreSubVector(v, fis, &fv));
377       PetscCall(ISDestroy(&fis));
378       PetscCall(DMDestroy(&fdm));
379     }
380   }
381   PetscFunctionReturn(0);
382 }
383 
384 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
385 {
386   DM        dm;
387   PetscDraw draw;
388   PetscInt  dim;
389   PetscBool isnull;
390 
391   PetscFunctionBegin;
392   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
393   PetscCall(PetscDrawIsNull(draw, &isnull));
394   if (isnull) PetscFunctionReturn(0);
395 
396   PetscCall(VecGetDM(v, &dm));
397   PetscCall(DMGetCoordinateDim(dm, &dim));
398   switch (dim) {
399   case 1:
400     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
401     break;
402   case 2:
403     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
404     break;
405   default:
406     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
407   }
408   PetscFunctionReturn(0);
409 }
410 
411 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
412 {
413   DM                      dm;
414   Vec                     locv;
415   const char             *name;
416   PetscSection            section;
417   PetscInt                pStart, pEnd;
418   PetscInt                numFields;
419   PetscViewerVTKFieldType ft;
420 
421   PetscFunctionBegin;
422   PetscCall(VecGetDM(v, &dm));
423   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
424   PetscCall(PetscObjectGetName((PetscObject)v, &name));
425   PetscCall(PetscObjectSetName((PetscObject)locv, name));
426   PetscCall(VecCopy(v, locv));
427   PetscCall(DMGetLocalSection(dm, &section));
428   PetscCall(PetscSectionGetNumFields(section, &numFields));
429   if (!numFields) {
430     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
431     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
432   } else {
433     PetscInt f;
434 
435     for (f = 0; f < numFields; f++) {
436       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
437       if (ft == PETSC_VTK_INVALID) continue;
438       PetscCall(PetscObjectReference((PetscObject)locv));
439       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
440     }
441     PetscCall(VecDestroy(&locv));
442   }
443   PetscFunctionReturn(0);
444 }
445 
446 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
447 {
448   DM        dm;
449   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
450 
451   PetscFunctionBegin;
452   PetscCall(VecGetDM(v, &dm));
453   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
454   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
455   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
456   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
457   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
458   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
459   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
460     PetscInt    i, numFields;
461     PetscObject fe;
462     PetscBool   fem  = PETSC_FALSE;
463     Vec         locv = v;
464     const char *name;
465     PetscInt    step;
466     PetscReal   time;
467 
468     PetscCall(DMGetNumFields(dm, &numFields));
469     for (i = 0; i < numFields; i++) {
470       PetscCall(DMGetField(dm, i, NULL, &fe));
471       if (fe->classid == PETSCFE_CLASSID) {
472         fem = PETSC_TRUE;
473         break;
474       }
475     }
476     if (fem) {
477       PetscObject isZero;
478 
479       PetscCall(DMGetLocalVector(dm, &locv));
480       PetscCall(PetscObjectGetName((PetscObject)v, &name));
481       PetscCall(PetscObjectSetName((PetscObject)locv, name));
482       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
483       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
484       PetscCall(VecCopy(v, locv));
485       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
486       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
487     }
488     if (isvtk) {
489       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
490     } else if (ishdf5) {
491 #if defined(PETSC_HAVE_HDF5)
492       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
493 #else
494       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
495 #endif
496     } else if (isdraw) {
497       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
498     } else if (isglvis) {
499       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
500       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
501       PetscCall(VecView_GLVis(locv, viewer));
502     } else if (iscgns) {
503 #if defined(PETSC_HAVE_CGNS)
504       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
505 #else
506       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
507 #endif
508     }
509     if (fem) {
510       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
511       PetscCall(DMRestoreLocalVector(dm, &locv));
512     }
513   } else {
514     PetscBool isseq;
515 
516     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
517     if (isseq) PetscCall(VecView_Seq(v, viewer));
518     else PetscCall(VecView_MPI(v, viewer));
519   }
520   PetscFunctionReturn(0);
521 }
522 
523 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
524 {
525   DM        dm;
526   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
527 
528   PetscFunctionBegin;
529   PetscCall(VecGetDM(v, &dm));
530   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
532   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
533   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
534   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
535   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
536   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
537   if (isvtk || isdraw || isglvis || iscgns) {
538     Vec         locv;
539     PetscObject isZero;
540     const char *name;
541 
542     PetscCall(DMGetLocalVector(dm, &locv));
543     PetscCall(PetscObjectGetName((PetscObject)v, &name));
544     PetscCall(PetscObjectSetName((PetscObject)locv, name));
545     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
546     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
547     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
548     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
549     PetscCall(VecView_Plex_Local(locv, viewer));
550     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
551     PetscCall(DMRestoreLocalVector(dm, &locv));
552   } else if (ishdf5) {
553 #if defined(PETSC_HAVE_HDF5)
554     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
555 #else
556     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
557 #endif
558   } else if (isexodusii) {
559 #if defined(PETSC_HAVE_EXODUSII)
560     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
561 #else
562     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
563 #endif
564   } else {
565     PetscBool isseq;
566 
567     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
568     if (isseq) PetscCall(VecView_Seq(v, viewer));
569     else PetscCall(VecView_MPI(v, viewer));
570   }
571   PetscFunctionReturn(0);
572 }
573 
574 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
575 {
576   DM                dm;
577   MPI_Comm          comm;
578   PetscViewerFormat format;
579   Vec               v;
580   PetscBool         isvtk, ishdf5;
581 
582   PetscFunctionBegin;
583   PetscCall(VecGetDM(originalv, &dm));
584   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
585   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
586   PetscCall(PetscViewerGetFormat(viewer, &format));
587   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
588   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
589   if (format == PETSC_VIEWER_NATIVE) {
590     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
591     /* this need a better fix */
592     if (dm->useNatural) {
593       if (dm->sfNatural) {
594         const char *vecname;
595         PetscInt    n, nroots;
596 
597         PetscCall(VecGetLocalSize(originalv, &n));
598         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
599         if (n == nroots) {
600           PetscCall(DMGetGlobalVector(dm, &v));
601           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
602           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
603           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
604           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
605         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
606       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
607     } else v = originalv;
608   } else v = originalv;
609 
610   if (ishdf5) {
611 #if defined(PETSC_HAVE_HDF5)
612     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
613 #else
614     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
615 #endif
616   } else if (isvtk) {
617     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
618   } else {
619     PetscBool isseq;
620 
621     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
622     if (isseq) PetscCall(VecView_Seq(v, viewer));
623     else PetscCall(VecView_MPI(v, viewer));
624   }
625   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
626   PetscFunctionReturn(0);
627 }
628 
629 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
630 {
631   DM        dm;
632   PetscBool ishdf5;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
638   if (ishdf5) {
639     DM          dmBC;
640     Vec         gv;
641     const char *name;
642 
643     PetscCall(DMGetOutputDM(dm, &dmBC));
644     PetscCall(DMGetGlobalVector(dmBC, &gv));
645     PetscCall(PetscObjectGetName((PetscObject)v, &name));
646     PetscCall(PetscObjectSetName((PetscObject)gv, name));
647     PetscCall(VecLoad_Default(gv, viewer));
648     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
649     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
650     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
651   } else PetscCall(VecLoad_Default(v, viewer));
652   PetscFunctionReturn(0);
653 }
654 
655 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
656 {
657   DM        dm;
658   PetscBool ishdf5, isexodusii;
659 
660   PetscFunctionBegin;
661   PetscCall(VecGetDM(v, &dm));
662   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
663   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
664   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
665   if (ishdf5) {
666 #if defined(PETSC_HAVE_HDF5)
667     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
668 #else
669     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
670 #endif
671   } else if (isexodusii) {
672 #if defined(PETSC_HAVE_EXODUSII)
673     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
674 #else
675     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
676 #endif
677   } else PetscCall(VecLoad_Default(v, viewer));
678   PetscFunctionReturn(0);
679 }
680 
681 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
682 {
683   DM                dm;
684   PetscViewerFormat format;
685   PetscBool         ishdf5;
686 
687   PetscFunctionBegin;
688   PetscCall(VecGetDM(originalv, &dm));
689   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
690   PetscCall(PetscViewerGetFormat(viewer, &format));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
692   if (format == PETSC_VIEWER_NATIVE) {
693     if (dm->useNatural) {
694       if (dm->sfNatural) {
695         if (ishdf5) {
696 #if defined(PETSC_HAVE_HDF5)
697           Vec         v;
698           const char *vecname;
699 
700           PetscCall(DMGetGlobalVector(dm, &v));
701           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
702           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
703           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
704           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
705           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
706           PetscCall(DMRestoreGlobalVector(dm, &v));
707 #else
708           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
709 #endif
710         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
711       }
712     } else PetscCall(VecLoad_Default(originalv, viewer));
713   }
714   PetscFunctionReturn(0);
715 }
716 
717 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
718 {
719   PetscSection       coordSection;
720   Vec                coordinates;
721   DMLabel            depthLabel, celltypeLabel;
722   const char        *name[4];
723   const PetscScalar *a;
724   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
725 
726   PetscFunctionBegin;
727   PetscCall(DMGetDimension(dm, &dim));
728   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
729   PetscCall(DMGetCoordinateSection(dm, &coordSection));
730   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
731   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
732   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
733   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
734   PetscCall(VecGetArrayRead(coordinates, &a));
735   name[0]       = "vertex";
736   name[1]       = "edge";
737   name[dim - 1] = "face";
738   name[dim]     = "cell";
739   for (c = cStart; c < cEnd; ++c) {
740     PetscInt *closure = NULL;
741     PetscInt  closureSize, cl, ct;
742 
743     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
744     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
745     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
746     PetscCall(PetscViewerASCIIPushTab(viewer));
747     for (cl = 0; cl < closureSize * 2; cl += 2) {
748       PetscInt point = closure[cl], depth, dof, off, d, p;
749 
750       if ((point < pStart) || (point >= pEnd)) continue;
751       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
752       if (!dof) continue;
753       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
754       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
755       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
756       for (p = 0; p < dof / dim; ++p) {
757         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
758         for (d = 0; d < dim; ++d) {
759           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
760           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
761         }
762         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
763       }
764       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
765     }
766     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
767     PetscCall(PetscViewerASCIIPopTab(viewer));
768   }
769   PetscCall(VecRestoreArrayRead(coordinates, &a));
770   PetscFunctionReturn(0);
771 }
772 
773 typedef enum {
774   CS_CARTESIAN,
775   CS_POLAR,
776   CS_CYLINDRICAL,
777   CS_SPHERICAL
778 } CoordSystem;
779 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
780 
781 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
782 {
783   PetscInt i;
784 
785   PetscFunctionBegin;
786   if (dim > 3) {
787     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
788   } else {
789     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
790 
791     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
792     switch (cs) {
793     case CS_CARTESIAN:
794       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
795       break;
796     case CS_POLAR:
797       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       break;
801     case CS_CYLINDRICAL:
802       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
803       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
804       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
805       trcoords[2] = coords[2];
806       break;
807     case CS_SPHERICAL:
808       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
809       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
810       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
811       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
812       break;
813     }
814     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
815   }
816   PetscFunctionReturn(0);
817 }
818 
819 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
820 {
821   DM_Plex          *mesh = (DM_Plex *)dm->data;
822   DM                cdm, cdmCell;
823   PetscSection      coordSection, coordSectionCell;
824   Vec               coordinates, coordinatesCell;
825   PetscViewerFormat format;
826 
827   PetscFunctionBegin;
828   PetscCall(PetscViewerGetFormat(viewer, &format));
829   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
830     const char *name;
831     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
832     PetscInt    pStart, pEnd, p, numLabels, l;
833     PetscMPIInt rank, size;
834 
835     PetscCall(DMGetCoordinateDM(dm, &cdm));
836     PetscCall(DMGetCoordinateSection(dm, &coordSection));
837     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
838     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
839     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
840     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
841     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
842     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
843     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
844     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
845     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
846     PetscCall(DMGetDimension(dm, &dim));
847     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
848     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
849     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
850     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
851     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
852     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
853     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
854     for (p = pStart; p < pEnd; ++p) {
855       PetscInt dof, off, s;
856 
857       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
858       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
859       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
860     }
861     PetscCall(PetscViewerFlush(viewer));
862     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
863     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
864     for (p = pStart; p < pEnd; ++p) {
865       PetscInt dof, off, c;
866 
867       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
868       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
869       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
870     }
871     PetscCall(PetscViewerFlush(viewer));
872     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
873     if (coordSection && coordinates) {
874       CoordSystem        cs = CS_CARTESIAN;
875       const PetscScalar *array, *arrayCell = NULL;
876       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
877       PetscMPIInt        rank;
878       const char        *name;
879 
880       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
881       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
882       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
883       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
884       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
885       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
886       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
887       pStart = PetscMin(pvStart, pcStart);
888       pEnd   = PetscMax(pvEnd, pcEnd);
889       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
890       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
891       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
892       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
893 
894       PetscCall(VecGetArrayRead(coordinates, &array));
895       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
896       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
897       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
898       for (p = pStart; p < pEnd; ++p) {
899         PetscInt dof, off;
900 
901         if (p >= pvStart && p < pvEnd) {
902           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
903           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
904           if (dof) {
905             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
906             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
907             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
908           }
909         }
910         if (cdmCell && p >= pcStart && p < pcEnd) {
911           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
912           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
913           if (dof) {
914             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
915             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
916             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
917           }
918         }
919       }
920       PetscCall(PetscViewerFlush(viewer));
921       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
922       PetscCall(VecRestoreArrayRead(coordinates, &array));
923       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
924     }
925     PetscCall(DMGetNumLabels(dm, &numLabels));
926     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
927     for (l = 0; l < numLabels; ++l) {
928       DMLabel     label;
929       PetscBool   isdepth;
930       const char *name;
931 
932       PetscCall(DMGetLabelName(dm, l, &name));
933       PetscCall(PetscStrcmp(name, "depth", &isdepth));
934       if (isdepth) continue;
935       PetscCall(DMGetLabel(dm, name, &label));
936       PetscCall(DMLabelView(label, viewer));
937     }
938     if (size > 1) {
939       PetscSF sf;
940 
941       PetscCall(DMGetPointSF(dm, &sf));
942       PetscCall(PetscSFView(sf, viewer));
943     }
944     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
945     PetscCall(PetscViewerFlush(viewer));
946   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
947     const char  *name, *color;
948     const char  *defcolors[3]  = {"gray", "orange", "green"};
949     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
950     char         lname[PETSC_MAX_PATH_LEN];
951     PetscReal    scale      = 2.0;
952     PetscReal    tikzscale  = 1.0;
953     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
954     double       tcoords[3];
955     PetscScalar *coords;
956     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
957     PetscMPIInt  rank, size;
958     char       **names, **colors, **lcolors;
959     PetscBool    flg, lflg;
960     PetscBT      wp = NULL;
961     PetscInt     pEnd, pStart;
962 
963     PetscCall(DMGetCoordinateDM(dm, &cdm));
964     PetscCall(DMGetCoordinateSection(dm, &coordSection));
965     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
966     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
967     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
968     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
969     PetscCall(DMGetDimension(dm, &dim));
970     PetscCall(DMPlexGetDepth(dm, &depth));
971     PetscCall(DMGetNumLabels(dm, &numLabels));
972     numLabels  = PetscMax(numLabels, 10);
973     numColors  = 10;
974     numLColors = 10;
975     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
976     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
977     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
978     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
979     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
980     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
981     n = 4;
982     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
983     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
984     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
985     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
986     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
987     if (!useLabels) numLabels = 0;
988     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
989     if (!useColors) {
990       numColors = 3;
991       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
992     }
993     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
994     if (!useColors) {
995       numLColors = 4;
996       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
997     }
998     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
999     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1000     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1001     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1002     if (depth < dim) plotEdges = PETSC_FALSE;
1003     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1004 
1005     /* filter points with labelvalue != labeldefaultvalue */
1006     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1007     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1008     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1009     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1010     if (lflg) {
1011       DMLabel lbl;
1012 
1013       PetscCall(DMGetLabel(dm, lname, &lbl));
1014       if (lbl) {
1015         PetscInt val, defval;
1016 
1017         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1018         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1019         for (c = pStart; c < pEnd; c++) {
1020           PetscInt *closure = NULL;
1021           PetscInt  closureSize;
1022 
1023           PetscCall(DMLabelGetValue(lbl, c, &val));
1024           if (val == defval) continue;
1025 
1026           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1027           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1028           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1029         }
1030       }
1031     }
1032 
1033     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1034     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1035     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1036     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1037 \\documentclass[tikz]{standalone}\n\n\
1038 \\usepackage{pgflibraryshapes}\n\
1039 \\usetikzlibrary{backgrounds}\n\
1040 \\usetikzlibrary{arrows}\n\
1041 \\begin{document}\n"));
1042     if (size > 1) {
1043       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1044       for (p = 0; p < size; ++p) {
1045         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1046         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1047       }
1048       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1049     }
1050     if (drawHasse) {
1051       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1052 
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1060       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1061       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1062       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1063       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1064       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1065     }
1066     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1067 
1068     /* Plot vertices */
1069     PetscCall(VecGetArray(coordinates, &coords));
1070     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1071     for (v = vStart; v < vEnd; ++v) {
1072       PetscInt  off, dof, d;
1073       PetscBool isLabeled = PETSC_FALSE;
1074 
1075       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1076       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1077       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1078       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1079       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1080       for (d = 0; d < dof; ++d) {
1081         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1082         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1083       }
1084       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1085       if (dim == 3) {
1086         PetscReal tmp = tcoords[1];
1087         tcoords[1]    = tcoords[2];
1088         tcoords[2]    = -tmp;
1089       }
1090       for (d = 0; d < dof; ++d) {
1091         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1092         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1093       }
1094       if (drawHasse) color = colors[0 % numColors];
1095       else color = colors[rank % numColors];
1096       for (l = 0; l < numLabels; ++l) {
1097         PetscInt val;
1098         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1099         if (val >= 0) {
1100           color     = lcolors[l % numLColors];
1101           isLabeled = PETSC_TRUE;
1102           break;
1103         }
1104       }
1105       if (drawNumbers[0]) {
1106         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1107       } else if (drawColors[0]) {
1108         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1109       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1110     }
1111     PetscCall(VecRestoreArray(coordinates, &coords));
1112     PetscCall(PetscViewerFlush(viewer));
1113     /* Plot edges */
1114     if (plotEdges) {
1115       PetscCall(VecGetArray(coordinates, &coords));
1116       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1117       for (e = eStart; e < eEnd; ++e) {
1118         const PetscInt *cone;
1119         PetscInt        coneSize, offA, offB, dof, d;
1120 
1121         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1122         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1123         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1124         PetscCall(DMPlexGetCone(dm, e, &cone));
1125         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1126         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1127         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1128         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1129         for (d = 0; d < dof; ++d) {
1130           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1131           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1132         }
1133         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1134         if (dim == 3) {
1135           PetscReal tmp = tcoords[1];
1136           tcoords[1]    = tcoords[2];
1137           tcoords[2]    = -tmp;
1138         }
1139         for (d = 0; d < dof; ++d) {
1140           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1141           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1142         }
1143         if (drawHasse) color = colors[1 % numColors];
1144         else color = colors[rank % numColors];
1145         for (l = 0; l < numLabels; ++l) {
1146           PetscInt val;
1147           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1148           if (val >= 0) {
1149             color = lcolors[l % numLColors];
1150             break;
1151           }
1152         }
1153         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1154       }
1155       PetscCall(VecRestoreArray(coordinates, &coords));
1156       PetscCall(PetscViewerFlush(viewer));
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1158     }
1159     /* Plot cells */
1160     if (dim == 3 || !drawNumbers[1]) {
1161       for (e = eStart; e < eEnd; ++e) {
1162         const PetscInt *cone;
1163 
1164         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1165         color = colors[rank % numColors];
1166         for (l = 0; l < numLabels; ++l) {
1167           PetscInt val;
1168           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1169           if (val >= 0) {
1170             color = lcolors[l % numLColors];
1171             break;
1172           }
1173         }
1174         PetscCall(DMPlexGetCone(dm, e, &cone));
1175         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1176       }
1177     } else {
1178       DMPolytopeType ct;
1179 
1180       /* Drawing a 2D polygon */
1181       for (c = cStart; c < cEnd; ++c) {
1182         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1183         PetscCall(DMPlexGetCellType(dm, c, &ct));
1184         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1185           const PetscInt *cone;
1186           PetscInt        coneSize, e;
1187 
1188           PetscCall(DMPlexGetCone(dm, c, &cone));
1189           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1190           for (e = 0; e < coneSize; ++e) {
1191             const PetscInt *econe;
1192 
1193             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1194             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));
1195           }
1196         } else {
1197           PetscInt *closure = NULL;
1198           PetscInt  closureSize, Nv = 0, v;
1199 
1200           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1201           for (p = 0; p < closureSize * 2; p += 2) {
1202             const PetscInt point = closure[p];
1203 
1204             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1205           }
1206           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1207           for (v = 0; v <= Nv; ++v) {
1208             const PetscInt vertex = closure[v % Nv];
1209 
1210             if (v > 0) {
1211               if (plotEdges) {
1212                 const PetscInt *edge;
1213                 PetscInt        endpoints[2], ne;
1214 
1215                 endpoints[0] = closure[v - 1];
1216                 endpoints[1] = vertex;
1217                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1218                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1219                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1220                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1221               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1222             }
1223             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1224           }
1225           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1226           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1227         }
1228       }
1229     }
1230     for (c = cStart; c < cEnd; ++c) {
1231       double             ccoords[3] = {0.0, 0.0, 0.0};
1232       PetscBool          isLabeled  = PETSC_FALSE;
1233       PetscScalar       *cellCoords = NULL;
1234       const PetscScalar *array;
1235       PetscInt           numCoords, cdim, d;
1236       PetscBool          isDG;
1237 
1238       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1239       PetscCall(DMGetCoordinateDim(dm, &cdim));
1240       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1241       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1242       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1243       for (p = 0; p < numCoords / cdim; ++p) {
1244         for (d = 0; d < cdim; ++d) {
1245           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1246           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1247         }
1248         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1249         if (cdim == 3) {
1250           PetscReal tmp = tcoords[1];
1251           tcoords[1]    = tcoords[2];
1252           tcoords[2]    = -tmp;
1253         }
1254         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1255       }
1256       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1257       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1258       for (d = 0; d < cdim; ++d) {
1259         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1260         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1261       }
1262       if (drawHasse) color = colors[depth % numColors];
1263       else color = colors[rank % numColors];
1264       for (l = 0; l < numLabels; ++l) {
1265         PetscInt val;
1266         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1267         if (val >= 0) {
1268           color     = lcolors[l % numLColors];
1269           isLabeled = PETSC_TRUE;
1270           break;
1271         }
1272       }
1273       if (drawNumbers[dim]) {
1274         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1275       } else if (drawColors[dim]) {
1276         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1277       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1278     }
1279     if (drawHasse) {
1280       color = colors[depth % numColors];
1281       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1282       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1286 
1287       color = colors[1 % numColors];
1288       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1289       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1293 
1294       color = colors[0 % numColors];
1295       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1296       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1297       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1298       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1299       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1300 
1301       for (p = pStart; p < pEnd; ++p) {
1302         const PetscInt *cone;
1303         PetscInt        coneSize, cp;
1304 
1305         PetscCall(DMPlexGetCone(dm, p, &cone));
1306         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1307         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1308       }
1309     }
1310     PetscCall(PetscViewerFlush(viewer));
1311     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1312     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1313     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1314     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1315     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1316     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1317     PetscCall(PetscFree3(names, colors, lcolors));
1318     PetscCall(PetscBTDestroy(&wp));
1319   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1320     Vec                    cown, acown;
1321     VecScatter             sct;
1322     ISLocalToGlobalMapping g2l;
1323     IS                     gid, acis;
1324     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1325     MPI_Group              ggroup, ngroup;
1326     PetscScalar           *array, nid;
1327     const PetscInt        *idxs;
1328     PetscInt              *idxs2, *start, *adjacency, *work;
1329     PetscInt64             lm[3], gm[3];
1330     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1331     PetscMPIInt            d1, d2, rank;
1332 
1333     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1334     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1335 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1336     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1337 #endif
1338     if (ncomm != MPI_COMM_NULL) {
1339       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1340       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1341       d1 = 0;
1342       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1343       nid = d2;
1344       PetscCallMPI(MPI_Group_free(&ggroup));
1345       PetscCallMPI(MPI_Group_free(&ngroup));
1346       PetscCallMPI(MPI_Comm_free(&ncomm));
1347     } else nid = 0.0;
1348 
1349     /* Get connectivity */
1350     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1351     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1352 
1353     /* filter overlapped local cells */
1354     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1355     PetscCall(ISGetIndices(gid, &idxs));
1356     PetscCall(ISGetLocalSize(gid, &cum));
1357     PetscCall(PetscMalloc1(cum, &idxs2));
1358     for (c = cStart, cum = 0; c < cEnd; c++) {
1359       if (idxs[c - cStart] < 0) continue;
1360       idxs2[cum++] = idxs[c - cStart];
1361     }
1362     PetscCall(ISRestoreIndices(gid, &idxs));
1363     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1364     PetscCall(ISDestroy(&gid));
1365     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1366 
1367     /* support for node-aware cell locality */
1368     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1369     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1370     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1371     PetscCall(VecGetArray(cown, &array));
1372     for (c = 0; c < numVertices; c++) array[c] = nid;
1373     PetscCall(VecRestoreArray(cown, &array));
1374     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1375     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1376     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1377     PetscCall(ISDestroy(&acis));
1378     PetscCall(VecScatterDestroy(&sct));
1379     PetscCall(VecDestroy(&cown));
1380 
1381     /* compute edgeCut */
1382     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1383     PetscCall(PetscMalloc1(cum, &work));
1384     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1385     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1386     PetscCall(ISDestroy(&gid));
1387     PetscCall(VecGetArray(acown, &array));
1388     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1389       PetscInt totl;
1390 
1391       totl = start[c + 1] - start[c];
1392       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1393       for (i = 0; i < totl; i++) {
1394         if (work[i] < 0) {
1395           ect += 1;
1396           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1397         }
1398       }
1399     }
1400     PetscCall(PetscFree(work));
1401     PetscCall(VecRestoreArray(acown, &array));
1402     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1403     lm[1] = -numVertices;
1404     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1405     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1406     lm[0] = ect;                     /* edgeCut */
1407     lm[1] = ectn;                    /* node-aware edgeCut */
1408     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1409     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1410     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1411 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1412     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1413 #else
1414     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1415 #endif
1416     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1417     PetscCall(PetscFree(start));
1418     PetscCall(PetscFree(adjacency));
1419     PetscCall(VecDestroy(&acown));
1420   } else {
1421     const char    *name;
1422     PetscInt      *sizes, *hybsizes, *ghostsizes;
1423     PetscInt       locDepth, depth, cellHeight, dim, d;
1424     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1425     PetscInt       numLabels, l, maxSize = 17;
1426     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1427     MPI_Comm       comm;
1428     PetscMPIInt    size, rank;
1429 
1430     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1431     PetscCallMPI(MPI_Comm_size(comm, &size));
1432     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1433     PetscCall(DMGetDimension(dm, &dim));
1434     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1435     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1436     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1437     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1438     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1439     PetscCall(DMPlexGetDepth(dm, &locDepth));
1440     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1441     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1442     gcNum = gcEnd - gcStart;
1443     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1444     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1445     for (d = 0; d <= depth; d++) {
1446       PetscInt Nc[2] = {0, 0}, ict;
1447 
1448       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1449       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1450       ict = ct0;
1451       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1452       ct0 = (DMPolytopeType)ict;
1453       for (p = pStart; p < pEnd; ++p) {
1454         DMPolytopeType ct;
1455 
1456         PetscCall(DMPlexGetCellType(dm, p, &ct));
1457         if (ct == ct0) ++Nc[0];
1458         else ++Nc[1];
1459       }
1460       if (size < maxSize) {
1461         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1462         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1463         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1464         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1465         for (p = 0; p < size; ++p) {
1466           if (rank == 0) {
1467             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1468             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1469             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1470           }
1471         }
1472       } else {
1473         PetscInt locMinMax[2];
1474 
1475         locMinMax[0] = Nc[0] + Nc[1];
1476         locMinMax[1] = Nc[0] + Nc[1];
1477         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1478         locMinMax[0] = Nc[1];
1479         locMinMax[1] = Nc[1];
1480         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1481         if (d == depth) {
1482           locMinMax[0] = gcNum;
1483           locMinMax[1] = gcNum;
1484           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1485         }
1486         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1487         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1488         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1489         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1490       }
1491       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1492     }
1493     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1494     {
1495       const PetscReal *maxCell;
1496       const PetscReal *L;
1497       PetscBool        localized;
1498 
1499       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1500       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1501       if (L || localized) {
1502         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1503         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1504         if (L) {
1505           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1506           for (d = 0; d < dim; ++d) {
1507             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1508             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1509           }
1510           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1511         }
1512         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1513         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1514       }
1515     }
1516     PetscCall(DMGetNumLabels(dm, &numLabels));
1517     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1518     for (l = 0; l < numLabels; ++l) {
1519       DMLabel         label;
1520       const char     *name;
1521       IS              valueIS;
1522       const PetscInt *values;
1523       PetscInt        numValues, v;
1524 
1525       PetscCall(DMGetLabelName(dm, l, &name));
1526       PetscCall(DMGetLabel(dm, name, &label));
1527       PetscCall(DMLabelGetNumValues(label, &numValues));
1528       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1529       PetscCall(DMLabelGetValueIS(label, &valueIS));
1530       PetscCall(ISGetIndices(valueIS, &values));
1531       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1532       for (v = 0; v < numValues; ++v) {
1533         PetscInt size;
1534 
1535         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1536         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1537         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1538       }
1539       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1540       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1541       PetscCall(ISRestoreIndices(valueIS, &values));
1542       PetscCall(ISDestroy(&valueIS));
1543     }
1544     {
1545       char    **labelNames;
1546       PetscInt  Nl = numLabels;
1547       PetscBool flg;
1548 
1549       PetscCall(PetscMalloc1(Nl, &labelNames));
1550       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1551       for (l = 0; l < Nl; ++l) {
1552         DMLabel label;
1553 
1554         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1555         if (flg) {
1556           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1557           PetscCall(DMLabelView(label, viewer));
1558         }
1559         PetscCall(PetscFree(labelNames[l]));
1560       }
1561       PetscCall(PetscFree(labelNames));
1562     }
1563     /* If no fields are specified, people do not want to see adjacency */
1564     if (dm->Nf) {
1565       PetscInt f;
1566 
1567       for (f = 0; f < dm->Nf; ++f) {
1568         const char *name;
1569 
1570         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1571         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1572         PetscCall(PetscViewerASCIIPushTab(viewer));
1573         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1574         if (dm->fields[f].adjacency[0]) {
1575           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1576           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1577         } else {
1578           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1579           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1580         }
1581         PetscCall(PetscViewerASCIIPopTab(viewer));
1582       }
1583     }
1584     PetscCall(DMGetCoarseDM(dm, &cdm));
1585     if (cdm) {
1586       PetscCall(PetscViewerASCIIPushTab(viewer));
1587       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1588       PetscCall(DMPlexView_Ascii(cdm, viewer));
1589       PetscCall(PetscViewerASCIIPopTab(viewer));
1590     }
1591   }
1592   PetscFunctionReturn(0);
1593 }
1594 
1595 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1596 {
1597   DMPolytopeType ct;
1598   PetscMPIInt    rank;
1599   PetscInt       cdim;
1600 
1601   PetscFunctionBegin;
1602   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1603   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1604   PetscCall(DMGetCoordinateDim(dm, &cdim));
1605   switch (ct) {
1606   case DM_POLYTOPE_SEGMENT:
1607   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1608     switch (cdim) {
1609     case 1: {
1610       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1611       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1612 
1613       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1614       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1615       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1616     } break;
1617     case 2: {
1618       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1619       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1620       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1621 
1622       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1623       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));
1624       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));
1625     } break;
1626     default:
1627       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1628     }
1629     break;
1630   case DM_POLYTOPE_TRIANGLE:
1631     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));
1632     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1633     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1635     break;
1636   case DM_POLYTOPE_QUADRILATERAL:
1637     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));
1638     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));
1639     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1640     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1641     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1642     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1643     break;
1644   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1645     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));
1646     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));
1647     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1648     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1649     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1650     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1651     break;
1652   case DM_POLYTOPE_FV_GHOST:
1653     break;
1654   default:
1655     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1656   }
1657   PetscFunctionReturn(0);
1658 }
1659 
1660 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1661 {
1662   DMPolytopeType ct;
1663   PetscReal      centroid[2] = {0., 0.};
1664   PetscMPIInt    rank;
1665   PetscInt       fillColor, v, e, d;
1666 
1667   PetscFunctionBegin;
1668   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1669   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1670   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1671   switch (ct) {
1672   case DM_POLYTOPE_TRIANGLE: {
1673     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1674 
1675     for (v = 0; v < 3; ++v) {
1676       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1677       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1678     }
1679     for (e = 0; e < 3; ++e) {
1680       refCoords[0] = refVertices[e * 2 + 0];
1681       refCoords[1] = refVertices[e * 2 + 1];
1682       for (d = 1; d <= edgeDiv; ++d) {
1683         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1684         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1685       }
1686       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1687       for (d = 0; d < edgeDiv; ++d) {
1688         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));
1689         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1690       }
1691     }
1692   } break;
1693   default:
1694     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1695   }
1696   PetscFunctionReturn(0);
1697 }
1698 
1699 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1700 {
1701   PetscDraw          draw;
1702   DM                 cdm;
1703   PetscSection       coordSection;
1704   Vec                coordinates;
1705   const PetscScalar *coords;
1706   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1707   PetscReal         *refCoords, *edgeCoords;
1708   PetscBool          isnull, drawAffine = PETSC_TRUE;
1709   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1710 
1711   PetscFunctionBegin;
1712   PetscCall(DMGetCoordinateDim(dm, &dim));
1713   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1714   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1715   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1716   PetscCall(DMGetCoordinateDM(dm, &cdm));
1717   PetscCall(DMGetLocalSection(cdm, &coordSection));
1718   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1719   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1720   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1721 
1722   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1723   PetscCall(PetscDrawIsNull(draw, &isnull));
1724   if (isnull) PetscFunctionReturn(0);
1725   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1726 
1727   PetscCall(VecGetLocalSize(coordinates, &N));
1728   PetscCall(VecGetArrayRead(coordinates, &coords));
1729   for (c = 0; c < N; c += dim) {
1730     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1731     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1732     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1733     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1734   }
1735   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1736   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1737   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1738   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1739   PetscCall(PetscDrawClear(draw));
1740 
1741   for (c = cStart; c < cEnd; ++c) {
1742     PetscScalar *coords = NULL;
1743     PetscInt     numCoords;
1744 
1745     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1746     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1747     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1748     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1749   }
1750   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1751   PetscCall(PetscDrawFlush(draw));
1752   PetscCall(PetscDrawPause(draw));
1753   PetscCall(PetscDrawSave(draw));
1754   PetscFunctionReturn(0);
1755 }
1756 
1757 #if defined(PETSC_HAVE_EXODUSII)
1758   #include <exodusII.h>
1759   #include <petscviewerexodusii.h>
1760 #endif
1761 
1762 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1763 {
1764   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1765   char      name[PETSC_MAX_PATH_LEN];
1766 
1767   PetscFunctionBegin;
1768   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1769   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1770   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1771   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1772   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1773   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1774   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1775   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1776   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1777   if (iascii) {
1778     PetscViewerFormat format;
1779     PetscCall(PetscViewerGetFormat(viewer, &format));
1780     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1781     else PetscCall(DMPlexView_Ascii(dm, viewer));
1782   } else if (ishdf5) {
1783 #if defined(PETSC_HAVE_HDF5)
1784     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1785 #else
1786     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1787 #endif
1788   } else if (isvtk) {
1789     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1790   } else if (isdraw) {
1791     PetscCall(DMPlexView_Draw(dm, viewer));
1792   } else if (isglvis) {
1793     PetscCall(DMPlexView_GLVis(dm, viewer));
1794 #if defined(PETSC_HAVE_EXODUSII)
1795   } else if (isexodus) {
1796     /*
1797       exodusII requires that all sets be part of exactly one cell set.
1798       If the dm does not have a "Cell Sets" label defined, we create one
1799       with ID 1, containig all cells.
1800       Note that if the Cell Sets label is defined but does not cover all cells,
1801       we may still have a problem. This should probably be checked here or in the viewer;
1802     */
1803     PetscInt numCS;
1804     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1805     if (!numCS) {
1806       PetscInt cStart, cEnd, c;
1807       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1808       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1809       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1810     }
1811     PetscCall(DMView_PlexExodusII(dm, viewer));
1812 #endif
1813 #if defined(PETSC_HAVE_CGNS)
1814   } else if (iscgns) {
1815     PetscCall(DMView_PlexCGNS(dm, viewer));
1816 #endif
1817   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1818   /* Optionally view the partition */
1819   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1820   if (flg) {
1821     Vec ranks;
1822     PetscCall(DMPlexCreateRankField(dm, &ranks));
1823     PetscCall(VecView(ranks, viewer));
1824     PetscCall(VecDestroy(&ranks));
1825   }
1826   /* Optionally view a label */
1827   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1828   if (flg) {
1829     DMLabel label;
1830     Vec     val;
1831 
1832     PetscCall(DMGetLabel(dm, name, &label));
1833     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1834     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1835     PetscCall(VecView(val, viewer));
1836     PetscCall(VecDestroy(&val));
1837   }
1838   PetscFunctionReturn(0);
1839 }
1840 
1841 /*@
1842   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1843 
1844   Collective on dm
1845 
1846   Input Parameters:
1847 + dm     - The `DM` whose topology is to be saved
1848 - viewer - The `PetscViewer` to save it in
1849 
1850   Level: advanced
1851 
1852 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1853 @*/
1854 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1855 {
1856   PetscBool ishdf5;
1857 
1858   PetscFunctionBegin;
1859   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1860   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1861   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1862   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1863   if (ishdf5) {
1864 #if defined(PETSC_HAVE_HDF5)
1865     PetscViewerFormat format;
1866     PetscCall(PetscViewerGetFormat(viewer, &format));
1867     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1868       IS globalPointNumbering;
1869 
1870       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1871       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1872       PetscCall(ISDestroy(&globalPointNumbering));
1873     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1874 #else
1875     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1876 #endif
1877   }
1878   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1879   PetscFunctionReturn(0);
1880 }
1881 
1882 /*@
1883   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1884 
1885   Collective on dm
1886 
1887   Input Parameters:
1888 + dm     - The `DM` whose coordinates are to be saved
1889 - viewer - The `PetscViewer` for saving
1890 
1891   Level: advanced
1892 
1893 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1894 @*/
1895 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1896 {
1897   PetscBool ishdf5;
1898 
1899   PetscFunctionBegin;
1900   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1901   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1902   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1903   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1904   if (ishdf5) {
1905 #if defined(PETSC_HAVE_HDF5)
1906     PetscViewerFormat format;
1907     PetscCall(PetscViewerGetFormat(viewer, &format));
1908     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1909       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1910     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1911 #else
1912     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1913 #endif
1914   }
1915   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1916   PetscFunctionReturn(0);
1917 }
1918 
1919 /*@
1920   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1921 
1922   Collective on dm
1923 
1924   Input Parameters:
1925 + dm     - The `DM` whose labels are to be saved
1926 - viewer - The `PetscViewer` for saving
1927 
1928   Level: advanced
1929 
1930 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1931 @*/
1932 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1933 {
1934   PetscBool ishdf5;
1935 
1936   PetscFunctionBegin;
1937   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1938   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1939   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1940   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1941   if (ishdf5) {
1942 #if defined(PETSC_HAVE_HDF5)
1943     IS                globalPointNumbering;
1944     PetscViewerFormat format;
1945 
1946     PetscCall(PetscViewerGetFormat(viewer, &format));
1947     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1948       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1949       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1950       PetscCall(ISDestroy(&globalPointNumbering));
1951     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1952 #else
1953     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1954 #endif
1955   }
1956   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1957   PetscFunctionReturn(0);
1958 }
1959 
1960 /*@
1961   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1962 
1963   Collective on dm
1964 
1965   Input Parameters:
1966 + dm         - The `DM` that contains the topology on which the section to be saved is defined
1967 . viewer     - The `PetscViewer` for saving
1968 - sectiondm  - The `DM` that contains the section to be saved
1969 
1970   Level: advanced
1971 
1972   Notes:
1973   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.
1974 
1975   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.
1976 
1977 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1978 @*/
1979 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1980 {
1981   PetscBool ishdf5;
1982 
1983   PetscFunctionBegin;
1984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1985   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1986   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1987   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1988   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1989   if (ishdf5) {
1990 #if defined(PETSC_HAVE_HDF5)
1991     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1992 #else
1993     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1994 #endif
1995   }
1996   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1997   PetscFunctionReturn(0);
1998 }
1999 
2000 /*@
2001   DMPlexGlobalVectorView - Saves a global vector
2002 
2003   Collective on dm
2004 
2005   Input Parameters:
2006 + dm        - The `DM` that represents the topology
2007 . viewer    - The `PetscViewer` to save data with
2008 . sectiondm - The `DM` that contains the global section on which vec is defined
2009 - vec       - The global vector to be saved
2010 
2011   Level: advanced
2012 
2013   Notes:
2014   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.
2015 
2016   Typical calling sequence:
2017 .vb
2018        DMCreate(PETSC_COMM_WORLD, &dm);
2019        DMSetType(dm, DMPLEX);
2020        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2021        DMClone(dm, &sectiondm);
2022        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2023        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2024        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2025        PetscSectionSetChart(section, pStart, pEnd);
2026        PetscSectionSetUp(section);
2027        DMSetLocalSection(sectiondm, section);
2028        PetscSectionDestroy(&section);
2029        DMGetGlobalVector(sectiondm, &vec);
2030        PetscObjectSetName((PetscObject)vec, "vec_name");
2031        DMPlexTopologyView(dm, viewer);
2032        DMPlexSectionView(dm, viewer, sectiondm);
2033        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2034        DMRestoreGlobalVector(sectiondm, &vec);
2035        DMDestroy(&sectiondm);
2036        DMDestroy(&dm);
2037 .ve
2038 
2039 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2040 @*/
2041 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2042 {
2043   PetscBool ishdf5;
2044 
2045   PetscFunctionBegin;
2046   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2047   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2048   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2049   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2050   /* Check consistency */
2051   {
2052     PetscSection section;
2053     PetscBool    includesConstraints;
2054     PetscInt     m, m1;
2055 
2056     PetscCall(VecGetLocalSize(vec, &m1));
2057     PetscCall(DMGetGlobalSection(sectiondm, &section));
2058     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2059     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2060     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2061     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2062   }
2063   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2064   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2065   if (ishdf5) {
2066 #if defined(PETSC_HAVE_HDF5)
2067     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2068 #else
2069     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2070 #endif
2071   }
2072   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2073   PetscFunctionReturn(0);
2074 }
2075 
2076 /*@
2077   DMPlexLocalVectorView - Saves a local vector
2078 
2079   Collective on dm
2080 
2081   Input Parameters:
2082 + dm        - The `DM` that represents the topology
2083 . viewer    - The `PetscViewer` to save data with
2084 . sectiondm - The `DM` that contains the local section on which vec is defined; may be the same as dm
2085 - vec       - The local vector to be saved
2086 
2087   Level: advanced
2088 
2089   Note:
2090   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.
2091 
2092   Typical calling sequence:
2093 .vb
2094        DMCreate(PETSC_COMM_WORLD, &dm);
2095        DMSetType(dm, DMPLEX);
2096        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2097        DMClone(dm, &sectiondm);
2098        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2099        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2100        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2101        PetscSectionSetChart(section, pStart, pEnd);
2102        PetscSectionSetUp(section);
2103        DMSetLocalSection(sectiondm, section);
2104        DMGetLocalVector(sectiondm, &vec);
2105        PetscObjectSetName((PetscObject)vec, "vec_name");
2106        DMPlexTopologyView(dm, viewer);
2107        DMPlexSectionView(dm, viewer, sectiondm);
2108        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2109        DMRestoreLocalVector(sectiondm, &vec);
2110        DMDestroy(&sectiondm);
2111        DMDestroy(&dm);
2112 .ve
2113 
2114 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2115 @*/
2116 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2117 {
2118   PetscBool ishdf5;
2119 
2120   PetscFunctionBegin;
2121   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2122   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2123   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2124   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2125   /* Check consistency */
2126   {
2127     PetscSection section;
2128     PetscBool    includesConstraints;
2129     PetscInt     m, m1;
2130 
2131     PetscCall(VecGetLocalSize(vec, &m1));
2132     PetscCall(DMGetLocalSection(sectiondm, &section));
2133     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2134     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2135     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2136     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2137   }
2138   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2139   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2140   if (ishdf5) {
2141 #if defined(PETSC_HAVE_HDF5)
2142     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2143 #else
2144     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2145 #endif
2146   }
2147   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2148   PetscFunctionReturn(0);
2149 }
2150 
2151 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2152 {
2153   PetscBool ishdf5;
2154 
2155   PetscFunctionBegin;
2156   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2157   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2158   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2159   if (ishdf5) {
2160 #if defined(PETSC_HAVE_HDF5)
2161     PetscViewerFormat format;
2162     PetscCall(PetscViewerGetFormat(viewer, &format));
2163     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2164       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2165     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2166       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2167     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2168     PetscFunctionReturn(0);
2169 #else
2170     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2171 #endif
2172   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2173 }
2174 
2175 /*@
2176   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2177 
2178   Collective on dm
2179 
2180   Input Parameters:
2181 + dm                - The `DM` into which the topology is loaded
2182 - viewer            - The `PetscViewer` for the saved topology
2183 
2184   Output Parameters:
2185 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
2186 
2187   Level: advanced
2188 
2189 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2190           `PetscViewer`, `PetscSF`
2191 @*/
2192 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2193 {
2194   PetscBool ishdf5;
2195 
2196   PetscFunctionBegin;
2197   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2198   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2199   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2200   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2201   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2202   if (ishdf5) {
2203 #if defined(PETSC_HAVE_HDF5)
2204     PetscViewerFormat format;
2205     PetscCall(PetscViewerGetFormat(viewer, &format));
2206     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2207       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2208     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2209 #else
2210     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2211 #endif
2212   }
2213   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2214   PetscFunctionReturn(0);
2215 }
2216 
2217 /*@
2218   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2219 
2220   Collective on dm
2221 
2222   Input Parameters:
2223 + dm     - The `DM` into which the coordinates are loaded
2224 . viewer - The `PetscViewer` for the saved coordinates
2225 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2226 
2227   Level: advanced
2228 
2229 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2230           `PetscSF`, `PetscViewer`
2231 @*/
2232 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2233 {
2234   PetscBool ishdf5;
2235 
2236   PetscFunctionBegin;
2237   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2238   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2239   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2240   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2241   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2242   if (ishdf5) {
2243 #if defined(PETSC_HAVE_HDF5)
2244     PetscViewerFormat format;
2245     PetscCall(PetscViewerGetFormat(viewer, &format));
2246     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2247       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2248     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2249 #else
2250     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2251 #endif
2252   }
2253   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2254   PetscFunctionReturn(0);
2255 }
2256 
2257 /*@
2258   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2259 
2260   Collective on dm
2261 
2262   Input Parameters:
2263 + dm     - The `DM` into which the labels are loaded
2264 . viewer - The `PetscViewer` for the saved labels
2265 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2266 
2267   Level: advanced
2268 
2269   Note:
2270   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2271 
2272 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2273           `PetscSF`, `PetscViewer`
2274 @*/
2275 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2276 {
2277   PetscBool ishdf5;
2278 
2279   PetscFunctionBegin;
2280   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2281   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2282   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2283   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2284   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2285   if (ishdf5) {
2286 #if defined(PETSC_HAVE_HDF5)
2287     PetscViewerFormat format;
2288 
2289     PetscCall(PetscViewerGetFormat(viewer, &format));
2290     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2291       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2292     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2293 #else
2294     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2295 #endif
2296   }
2297   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2298   PetscFunctionReturn(0);
2299 }
2300 
2301 /*@
2302   DMPlexSectionLoad - Loads section into a `DMPLEX`
2303 
2304   Collective on dm
2305 
2306   Input Parameters:
2307 + dm          - The `DM` that represents the topology
2308 . viewer      - The `PetscViewer` that represents the on-disk section (sectionA)
2309 . sectiondm   - The `DM` into which the on-disk section (sectionA) is migrated
2310 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2311 
2312   Output Parameters
2313 + globalDofSF - The SF that migrates any on-disk Vec data associated with sectionA into a global Vec associated with the sectiondm's global section (NULL if not needed)
2314 - localDofSF  - The SF that migrates any on-disk Vec data associated with sectionA into a local Vec associated with the sectiondm's local section (NULL if not needed)
2315 
2316   Level: advanced
2317 
2318   Notes:
2319   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.
2320 
2321   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.
2322 
2323   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.
2324 
2325   Example using 2 processes:
2326 .vb
2327   NX (number of points on dm): 4
2328   sectionA                   : the on-disk section
2329   vecA                       : a vector associated with sectionA
2330   sectionB                   : sectiondm's local section constructed in this function
2331   vecB (local)               : a vector associated with sectiondm's local section
2332   vecB (global)              : a vector associated with sectiondm's global section
2333 
2334                                      rank 0    rank 1
2335   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2336   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2337   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2338   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2339   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2340   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2341   sectionB->atlasDof             :     1 0 1 | 1 3
2342   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2343   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2344   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2345 .ve
2346   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2347 
2348 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2349 @*/
2350 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2351 {
2352   PetscBool ishdf5;
2353 
2354   PetscFunctionBegin;
2355   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2356   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2357   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2358   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2359   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2360   if (localDofSF) PetscValidPointer(localDofSF, 6);
2361   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2362   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2363   if (ishdf5) {
2364 #if defined(PETSC_HAVE_HDF5)
2365     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2366 #else
2367     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2368 #endif
2369   }
2370   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2371   PetscFunctionReturn(0);
2372 }
2373 
2374 /*@
2375   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2376 
2377   Collective on dm
2378 
2379   Input Parameters:
2380 + dm        - The `DM` that represents the topology
2381 . viewer    - The `PetscViewer` that represents the on-disk vector data
2382 . sectiondm - The `DM` that contains the global section on which vec is defined
2383 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2384 - vec       - The global vector to set values of
2385 
2386   Level: advanced
2387 
2388   Notes:
2389   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.
2390 
2391   Typical calling sequence:
2392 .vb
2393        DMCreate(PETSC_COMM_WORLD, &dm);
2394        DMSetType(dm, DMPLEX);
2395        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2396        DMPlexTopologyLoad(dm, viewer, &sfX);
2397        DMClone(dm, &sectiondm);
2398        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2399        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2400        DMGetGlobalVector(sectiondm, &vec);
2401        PetscObjectSetName((PetscObject)vec, "vec_name");
2402        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2403        DMRestoreGlobalVector(sectiondm, &vec);
2404        PetscSFDestroy(&gsf);
2405        PetscSFDestroy(&sfX);
2406        DMDestroy(&sectiondm);
2407        DMDestroy(&dm);
2408 .ve
2409 
2410 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2411           `PetscSF`, `PetscViewer`
2412 @*/
2413 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2414 {
2415   PetscBool ishdf5;
2416 
2417   PetscFunctionBegin;
2418   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2419   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2420   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2421   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2422   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2423   /* Check consistency */
2424   {
2425     PetscSection section;
2426     PetscBool    includesConstraints;
2427     PetscInt     m, m1;
2428 
2429     PetscCall(VecGetLocalSize(vec, &m1));
2430     PetscCall(DMGetGlobalSection(sectiondm, &section));
2431     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2432     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2433     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2434     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2435   }
2436   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2437   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2438   if (ishdf5) {
2439 #if defined(PETSC_HAVE_HDF5)
2440     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2441 #else
2442     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2443 #endif
2444   }
2445   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2446   PetscFunctionReturn(0);
2447 }
2448 
2449 /*@
2450   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2451 
2452   Collective on dm
2453 
2454   Input Parameters:
2455 + dm        - The `DM` that represents the topology
2456 . viewer    - The `PetscViewer` that represents the on-disk vector data
2457 . sectiondm - The `DM` that contains the local section on which vec is defined
2458 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2459 - vec       - The local vector to set values of
2460 
2461   Level: advanced
2462 
2463   Notes:
2464   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.
2465 
2466   Typical calling sequence:
2467 .vb
2468        DMCreate(PETSC_COMM_WORLD, &dm);
2469        DMSetType(dm, DMPLEX);
2470        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2471        DMPlexTopologyLoad(dm, viewer, &sfX);
2472        DMClone(dm, &sectiondm);
2473        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2474        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2475        DMGetLocalVector(sectiondm, &vec);
2476        PetscObjectSetName((PetscObject)vec, "vec_name");
2477        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2478        DMRestoreLocalVector(sectiondm, &vec);
2479        PetscSFDestroy(&lsf);
2480        PetscSFDestroy(&sfX);
2481        DMDestroy(&sectiondm);
2482        DMDestroy(&dm);
2483 .ve
2484 
2485 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2486           `PetscSF`, `PetscViewer`
2487 @*/
2488 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2489 {
2490   PetscBool ishdf5;
2491 
2492   PetscFunctionBegin;
2493   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2494   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2495   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2496   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2497   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2498   /* Check consistency */
2499   {
2500     PetscSection section;
2501     PetscBool    includesConstraints;
2502     PetscInt     m, m1;
2503 
2504     PetscCall(VecGetLocalSize(vec, &m1));
2505     PetscCall(DMGetLocalSection(sectiondm, &section));
2506     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2507     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2508     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2509     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2510   }
2511   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2512   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2513   if (ishdf5) {
2514 #if defined(PETSC_HAVE_HDF5)
2515     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2516 #else
2517     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2518 #endif
2519   }
2520   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2521   PetscFunctionReturn(0);
2522 }
2523 
2524 PetscErrorCode DMDestroy_Plex(DM dm)
2525 {
2526   DM_Plex *mesh = (DM_Plex *)dm->data;
2527 
2528   PetscFunctionBegin;
2529   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2530   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2531   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2532   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2533   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2534   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2535   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2536   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2537   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2538   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2539   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2540   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2541   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2542   if (--mesh->refct > 0) PetscFunctionReturn(0);
2543   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2544   PetscCall(PetscFree(mesh->cones));
2545   PetscCall(PetscFree(mesh->coneOrientations));
2546   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2547   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2548   PetscCall(PetscFree(mesh->supports));
2549   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2550   PetscCall(PetscFree(mesh->facesTmp));
2551   PetscCall(PetscFree(mesh->tetgenOpts));
2552   PetscCall(PetscFree(mesh->triangleOpts));
2553   PetscCall(PetscFree(mesh->transformType));
2554   PetscCall(PetscFree(mesh->distributionName));
2555   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2556   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2557   PetscCall(ISDestroy(&mesh->subpointIS));
2558   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2559   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2560   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2561   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2562   PetscCall(ISDestroy(&mesh->anchorIS));
2563   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2564   PetscCall(PetscFree(mesh->parents));
2565   PetscCall(PetscFree(mesh->childIDs));
2566   PetscCall(PetscSectionDestroy(&mesh->childSection));
2567   PetscCall(PetscFree(mesh->children));
2568   PetscCall(DMDestroy(&mesh->referenceTree));
2569   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2570   PetscCall(PetscFree(mesh->neighbors));
2571   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2572   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2573   PetscCall(PetscFree(mesh));
2574   PetscFunctionReturn(0);
2575 }
2576 
2577 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2578 {
2579   PetscSection           sectionGlobal;
2580   PetscInt               bs = -1, mbs;
2581   PetscInt               localSize, localStart = 0;
2582   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2583   MatType                mtype;
2584   ISLocalToGlobalMapping ltog;
2585 
2586   PetscFunctionBegin;
2587   PetscCall(MatInitializePackage());
2588   mtype = dm->mattype;
2589   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2590   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2591   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2592   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2593   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2594   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2595   PetscCall(MatSetType(*J, mtype));
2596   PetscCall(MatSetFromOptions(*J));
2597   PetscCall(MatGetBlockSize(*J, &mbs));
2598   if (mbs > 1) bs = mbs;
2599   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2600   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2601   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2602   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2603   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2604   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2605   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2606   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2607   if (!isShell) {
2608     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2609     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2610     PetscInt  pStart, pEnd, p, dof, cdof;
2611 
2612     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2613 
2614     PetscCall(PetscCalloc1(localSize, &pblocks));
2615     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2616     for (p = pStart; p < pEnd; ++p) {
2617       PetscInt bdof, offset;
2618 
2619       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2620       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2621       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2622       for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2623       dof  = dof < 0 ? -(dof + 1) : dof;
2624       bdof = cdof && (dof - cdof) ? 1 : dof;
2625       if (dof) {
2626         if (bs < 0) {
2627           bs = bdof;
2628         } else if (bs != bdof) {
2629           bs = 1;
2630         }
2631       }
2632     }
2633     /* Must have same blocksize on all procs (some might have no points) */
2634     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2635     bsLocal[1] = bs;
2636     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2637     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2638     else bs = bsMinMax[0];
2639     bs = PetscMax(1, bs);
2640     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2641     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2642       PetscCall(MatSetBlockSize(*J, bs));
2643       PetscCall(MatSetUp(*J));
2644     } else {
2645       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2646       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2647       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2648     }
2649     { // Consolidate blocks
2650       PetscInt nblocks = 0;
2651       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2652         if (pblocks[i] == 0) continue;
2653         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2654         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]);
2655       }
2656       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2657     }
2658     PetscCall(PetscFree(pblocks));
2659   }
2660   PetscCall(MatSetDM(*J, dm));
2661   PetscFunctionReturn(0);
2662 }
2663 
2664 /*@
2665   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2666 
2667   Not Collective
2668 
2669   Input Parameter:
2670 . mesh - The `DMPLEX`
2671 
2672   Output Parameters:
2673 . subsection - The subdomain section
2674 
2675   Level: developer
2676 
2677 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `PetscSection`
2678 @*/
2679 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2680 {
2681   DM_Plex *mesh = (DM_Plex *)dm->data;
2682 
2683   PetscFunctionBegin;
2684   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2685   if (!mesh->subdomainSection) {
2686     PetscSection section;
2687     PetscSF      sf;
2688 
2689     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2690     PetscCall(DMGetLocalSection(dm, &section));
2691     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2692     PetscCall(PetscSFDestroy(&sf));
2693   }
2694   *subsection = mesh->subdomainSection;
2695   PetscFunctionReturn(0);
2696 }
2697 
2698 /*@
2699   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2700 
2701   Not Collective
2702 
2703   Input Parameter:
2704 . mesh - The `DMPLEX`
2705 
2706   Output Parameters:
2707 + pStart - The first mesh point
2708 - pEnd   - The upper bound for mesh points
2709 
2710   Level: beginner
2711 
2712 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2713 @*/
2714 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2715 {
2716   DM_Plex *mesh = (DM_Plex *)dm->data;
2717 
2718   PetscFunctionBegin;
2719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2720   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2721   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2722   PetscFunctionReturn(0);
2723 }
2724 
2725 /*@
2726   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2727 
2728   Not Collective
2729 
2730   Input Parameters:
2731 + mesh - The `DMPLEX`
2732 . pStart - The first mesh point
2733 - pEnd   - The upper bound for mesh points
2734 
2735   Level: beginner
2736 
2737 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2738 @*/
2739 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2740 {
2741   DM_Plex *mesh = (DM_Plex *)dm->data;
2742 
2743   PetscFunctionBegin;
2744   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2745   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2746   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2747   PetscFunctionReturn(0);
2748 }
2749 
2750 /*@
2751   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2752 
2753   Not Collective
2754 
2755   Input Parameters:
2756 + mesh - The `DMPLEX`
2757 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2758 
2759   Output Parameter:
2760 . size - The cone size for point p
2761 
2762   Level: beginner
2763 
2764 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2765 @*/
2766 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2767 {
2768   DM_Plex *mesh = (DM_Plex *)dm->data;
2769 
2770   PetscFunctionBegin;
2771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2772   PetscValidIntPointer(size, 3);
2773   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2774   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2775   PetscFunctionReturn(0);
2776 }
2777 
2778 /*@
2779   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2780 
2781   Not Collective
2782 
2783   Input Parameters:
2784 + mesh - The `DMPLEX`
2785 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2786 - size - The cone size for point p
2787 
2788   Level: beginner
2789 
2790   Note:
2791   This should be called after `DMPlexSetChart()`.
2792 
2793 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2794 @*/
2795 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2796 {
2797   DM_Plex *mesh = (DM_Plex *)dm->data;
2798 
2799   PetscFunctionBegin;
2800   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2801   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2802   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2803   PetscFunctionReturn(0);
2804 }
2805 
2806 /*@C
2807   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2808 
2809   Not Collective
2810 
2811   Input Parameters:
2812 + dm - The `DMPLEX`
2813 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2814 
2815   Output Parameter:
2816 . cone - An array of points which are on the in-edges for point p
2817 
2818   Level: beginner
2819 
2820   Fortran Note:
2821   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2822   `DMPlexRestoreCone()` is not needed/available in C.
2823 
2824 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2825 @*/
2826 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2827 {
2828   DM_Plex *mesh = (DM_Plex *)dm->data;
2829   PetscInt off;
2830 
2831   PetscFunctionBegin;
2832   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2833   PetscValidPointer(cone, 3);
2834   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2835   *cone = &mesh->cones[off];
2836   PetscFunctionReturn(0);
2837 }
2838 
2839 /*@C
2840   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2841 
2842   Not Collective
2843 
2844   Input Parameters:
2845 + dm - The `DMPLEX`
2846 - p - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2847 
2848   Output Parameters:
2849 + pConesSection - `PetscSection` describing the layout of pCones
2850 - pCones - An array of points which are on the in-edges for the point set p
2851 
2852   Level: intermediate
2853 
2854 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2855 @*/
2856 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2857 {
2858   PetscSection cs, newcs;
2859   PetscInt    *cones;
2860   PetscInt    *newarr = NULL;
2861   PetscInt     n;
2862 
2863   PetscFunctionBegin;
2864   PetscCall(DMPlexGetCones(dm, &cones));
2865   PetscCall(DMPlexGetConeSection(dm, &cs));
2866   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2867   if (pConesSection) *pConesSection = newcs;
2868   if (pCones) {
2869     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2870     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2871   }
2872   PetscFunctionReturn(0);
2873 }
2874 
2875 /*@
2876   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2877 
2878   Not Collective
2879 
2880   Input Parameters:
2881 + dm - The `DMPLEX`
2882 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2883 
2884   Output Parameter:
2885 . expandedPoints - An array of vertices recursively expanded from input points
2886 
2887   Level: advanced
2888 
2889   Notes:
2890   Like `DMPlexGetConeRecursive()` but returns only the 0-depth IS (i.e. vertices only) and no sections.
2891 
2892   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2893 
2894 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2895           `DMPlexGetDepth()`, `IS`
2896 @*/
2897 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2898 {
2899   IS      *expandedPointsAll;
2900   PetscInt depth;
2901 
2902   PetscFunctionBegin;
2903   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2904   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2905   PetscValidPointer(expandedPoints, 3);
2906   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2907   *expandedPoints = expandedPointsAll[0];
2908   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2909   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2910   PetscFunctionReturn(0);
2911 }
2912 
2913 /*@
2914   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).
2915 
2916   Not Collective
2917 
2918   Input Parameters:
2919 + dm - The `DMPLEX`
2920 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2921 
2922   Output Parameters:
2923 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2924 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2925 - sections - (optional) An array of sections which describe mappings from points to their cone points
2926 
2927   Level: advanced
2928 
2929   Notes:
2930   Like `DMPlexGetConeTuple()` but recursive.
2931 
2932   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.
2933   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2934 
2935   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:
2936   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2937   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2938 
2939 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2940           `DMPlexGetDepth()`, `PetscSection`, `IS`
2941 @*/
2942 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2943 {
2944   const PetscInt *arr0 = NULL, *cone = NULL;
2945   PetscInt       *arr = NULL, *newarr = NULL;
2946   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2947   IS             *expandedPoints_;
2948   PetscSection   *sections_;
2949 
2950   PetscFunctionBegin;
2951   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2952   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2953   if (depth) PetscValidIntPointer(depth, 3);
2954   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2955   if (sections) PetscValidPointer(sections, 5);
2956   PetscCall(ISGetLocalSize(points, &n));
2957   PetscCall(ISGetIndices(points, &arr0));
2958   PetscCall(DMPlexGetDepth(dm, &depth_));
2959   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2960   PetscCall(PetscCalloc1(depth_, &sections_));
2961   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2962   for (d = depth_ - 1; d >= 0; d--) {
2963     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2964     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2965     for (i = 0; i < n; i++) {
2966       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2967       if (arr[i] >= start && arr[i] < end) {
2968         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2969         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2970       } else {
2971         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2972       }
2973     }
2974     PetscCall(PetscSectionSetUp(sections_[d]));
2975     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2976     PetscCall(PetscMalloc1(newn, &newarr));
2977     for (i = 0; i < n; i++) {
2978       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2979       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2980       if (cn > 1) {
2981         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2982         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2983       } else {
2984         newarr[co] = arr[i];
2985       }
2986     }
2987     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2988     arr = newarr;
2989     n   = newn;
2990   }
2991   PetscCall(ISRestoreIndices(points, &arr0));
2992   *depth = depth_;
2993   if (expandedPoints) *expandedPoints = expandedPoints_;
2994   else {
2995     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2996     PetscCall(PetscFree(expandedPoints_));
2997   }
2998   if (sections) *sections = sections_;
2999   else {
3000     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3001     PetscCall(PetscFree(sections_));
3002   }
3003   PetscFunctionReturn(0);
3004 }
3005 
3006 /*@
3007   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3008 
3009   Not Collective
3010 
3011   Input Parameters:
3012 + dm - The `DMPLEX`
3013 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3014 
3015   Output Parameters:
3016 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3017 . expandedPoints - (optional) An array of recursively expanded cones
3018 - sections - (optional) An array of sections which describe mappings from points to their cone points
3019 
3020   Level: advanced
3021 
3022   Note:
3023   See `DMPlexGetConeRecursive()`
3024 
3025 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3026           `DMPlexGetDepth()`, `IS`, `PetscSection`
3027 @*/
3028 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3029 {
3030   PetscInt d, depth_;
3031 
3032   PetscFunctionBegin;
3033   PetscCall(DMPlexGetDepth(dm, &depth_));
3034   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3035   if (depth) *depth = 0;
3036   if (expandedPoints) {
3037     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3038     PetscCall(PetscFree(*expandedPoints));
3039   }
3040   if (sections) {
3041     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3042     PetscCall(PetscFree(*sections));
3043   }
3044   PetscFunctionReturn(0);
3045 }
3046 
3047 /*@
3048   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
3049 
3050   Not Collective
3051 
3052   Input Parameters:
3053 + mesh - The `DMPLEX`
3054 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3055 - cone - An array of points which are on the in-edges for point p
3056 
3057   Level: beginner
3058 
3059   Note:
3060   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3061 
3062 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3063 @*/
3064 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3065 {
3066   DM_Plex *mesh = (DM_Plex *)dm->data;
3067   PetscInt pStart, pEnd;
3068   PetscInt dof, off, c;
3069 
3070   PetscFunctionBegin;
3071   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3072   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3073   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3074   if (dof) PetscValidIntPointer(cone, 3);
3075   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3076   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);
3077   for (c = 0; c < dof; ++c) {
3078     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);
3079     mesh->cones[off + c] = cone[c];
3080   }
3081   PetscFunctionReturn(0);
3082 }
3083 
3084 /*@C
3085   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3086 
3087   Not Collective
3088 
3089   Input Parameters:
3090 + mesh - The `DMPLEX`
3091 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3092 
3093   Output Parameter:
3094 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3095                     integer giving the prescription for cone traversal.
3096 
3097   Level: beginner
3098 
3099   Note:
3100   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3101   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3102   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3103   with the identity.
3104 
3105   Fortran Note:
3106   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3107   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3108 
3109 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3110 @*/
3111 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3112 {
3113   DM_Plex *mesh = (DM_Plex *)dm->data;
3114   PetscInt off;
3115 
3116   PetscFunctionBegin;
3117   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3118   if (PetscDefined(USE_DEBUG)) {
3119     PetscInt dof;
3120     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3121     if (dof) PetscValidPointer(coneOrientation, 3);
3122   }
3123   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3124 
3125   *coneOrientation = &mesh->coneOrientations[off];
3126   PetscFunctionReturn(0);
3127 }
3128 
3129 /*@
3130   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3131 
3132   Not Collective
3133 
3134   Input Parameters:
3135 + mesh - The `DMPLEX`
3136 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3137 - coneOrientation - An array of orientations
3138 
3139   Level: beginner
3140 
3141   Notes:
3142   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3143 
3144   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3145 
3146 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3147 @*/
3148 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3149 {
3150   DM_Plex *mesh = (DM_Plex *)dm->data;
3151   PetscInt pStart, pEnd;
3152   PetscInt dof, off, c;
3153 
3154   PetscFunctionBegin;
3155   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3156   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3157   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3158   if (dof) PetscValidIntPointer(coneOrientation, 3);
3159   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3160   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);
3161   for (c = 0; c < dof; ++c) {
3162     PetscInt cdof, o = coneOrientation[c];
3163 
3164     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3165     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);
3166     mesh->coneOrientations[off + c] = o;
3167   }
3168   PetscFunctionReturn(0);
3169 }
3170 
3171 /*@
3172   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3173 
3174   Not Collective
3175 
3176   Input Parameters:
3177 + mesh - The `DMPLEX`
3178 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3179 . conePos - The local index in the cone where the point should be put
3180 - conePoint - The mesh point to insert
3181 
3182   Level: beginner
3183 
3184 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3185 @*/
3186 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3187 {
3188   DM_Plex *mesh = (DM_Plex *)dm->data;
3189   PetscInt pStart, pEnd;
3190   PetscInt dof, off;
3191 
3192   PetscFunctionBegin;
3193   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3194   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3195   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);
3196   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);
3197   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3198   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3199   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);
3200   mesh->cones[off + conePos] = conePoint;
3201   PetscFunctionReturn(0);
3202 }
3203 
3204 /*@
3205   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3206 
3207   Not Collective
3208 
3209   Input Parameters:
3210 + mesh - The `DMPLEX`
3211 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3212 . conePos - The local index in the cone where the point should be put
3213 - coneOrientation - The point orientation to insert
3214 
3215   Level: beginner
3216 
3217   Note:
3218   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3219 
3220 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3221 @*/
3222 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3223 {
3224   DM_Plex *mesh = (DM_Plex *)dm->data;
3225   PetscInt pStart, pEnd;
3226   PetscInt dof, off;
3227 
3228   PetscFunctionBegin;
3229   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3230   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3231   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);
3232   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3233   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3234   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);
3235   mesh->coneOrientations[off + conePos] = coneOrientation;
3236   PetscFunctionReturn(0);
3237 }
3238 
3239 /*@C
3240   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3241 
3242   Not collective
3243 
3244   Input Parameters:
3245 + dm - The DMPlex
3246 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3247 
3248   Output Parameters:
3249 + cone - An array of points which are on the in-edges for point p
3250 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3251         integer giving the prescription for cone traversal.
3252 
3253   Level: beginner
3254 
3255   Notes:
3256   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3257   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3258   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3259   with the identity.
3260 
3261   Fortran Notes:
3262   Since it returns an array, this routine is only available in Fortran 90, and you must
3263   include petsc.h90 in your code.
3264   You must also call DMPlexRestoreCone() after you finish using the returned array.
3265   DMPlexRestoreCone() is not needed/available in C.
3266 
3267 .seealso: `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3268 @*/
3269 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3270 {
3271   DM_Plex *mesh = (DM_Plex *)dm->data;
3272 
3273   PetscFunctionBegin;
3274   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3275   if (mesh->tr) {
3276     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3277   } else {
3278     PetscInt off;
3279     if (PetscDefined(USE_DEBUG)) {
3280       PetscInt dof;
3281       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3282       if (dof) {
3283         if (cone) PetscValidPointer(cone, 3);
3284         if (ornt) PetscValidPointer(ornt, 4);
3285       }
3286     }
3287     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3288     if (cone) *cone = &mesh->cones[off];
3289     if (ornt) *ornt = &mesh->coneOrientations[off];
3290   }
3291   PetscFunctionReturn(0);
3292 }
3293 
3294 /*@C
3295   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3296 
3297   Not collective
3298 
3299   Input Parameters:
3300 + dm - The DMPlex
3301 . p  - The point, which must lie in the chart set with DMPlexSetChart()
3302 . cone - An array of points which are on the in-edges for point p
3303 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3304         integer giving the prescription for cone traversal.
3305 
3306   Level: beginner
3307 
3308   Notes:
3309   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3310   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3311   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3312   with the identity.
3313 
3314   Fortran Notes:
3315   Since it returns an array, this routine is only available in Fortran 90, and you must
3316   include petsc.h90 in your code.
3317   You must also call DMPlexRestoreCone() after you finish using the returned array.
3318   DMPlexRestoreCone() is not needed/available in C.
3319 
3320 .seealso: `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3321 @*/
3322 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3323 {
3324   DM_Plex *mesh = (DM_Plex *)dm->data;
3325 
3326   PetscFunctionBegin;
3327   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3328   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3329   PetscFunctionReturn(0);
3330 }
3331 
3332 /*@
3333   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3334 
3335   Not Collective
3336 
3337   Input Parameters:
3338 + mesh - The `DMPLEX`
3339 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3340 
3341   Output Parameter:
3342 . size - The support size for point p
3343 
3344   Level: beginner
3345 
3346 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3347 @*/
3348 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3349 {
3350   DM_Plex *mesh = (DM_Plex *)dm->data;
3351 
3352   PetscFunctionBegin;
3353   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3354   PetscValidIntPointer(size, 3);
3355   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3356   PetscFunctionReturn(0);
3357 }
3358 
3359 /*@
3360   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3361 
3362   Not Collective
3363 
3364   Input Parameters:
3365 + mesh - The `DMPLEX`
3366 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3367 - size - The support size for point p
3368 
3369   Level: beginner
3370 
3371   Note:
3372   This should be called after DMPlexSetChart().
3373 
3374 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3375 @*/
3376 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3377 {
3378   DM_Plex *mesh = (DM_Plex *)dm->data;
3379 
3380   PetscFunctionBegin;
3381   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3382   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3383   PetscFunctionReturn(0);
3384 }
3385 
3386 /*@C
3387   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3388 
3389   Not Collective
3390 
3391   Input Parameters:
3392 + mesh - The `DMPLEX`
3393 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3394 
3395   Output Parameter:
3396 . support - An array of points which are on the out-edges for point p
3397 
3398   Level: beginner
3399 
3400   Fortran Note:
3401   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3402   `DMPlexRestoreSupport()` is not needed/available in C.
3403 
3404 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3405 @*/
3406 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3407 {
3408   DM_Plex *mesh = (DM_Plex *)dm->data;
3409   PetscInt off;
3410 
3411   PetscFunctionBegin;
3412   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3413   PetscValidPointer(support, 3);
3414   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3415   *support = &mesh->supports[off];
3416   PetscFunctionReturn(0);
3417 }
3418 
3419 /*@
3420   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3421 
3422   Not Collective
3423 
3424   Input Parameters:
3425 + mesh - The `DMPLEX`
3426 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3427 - support - An array of points which are on the out-edges for point p
3428 
3429   Level: beginner
3430 
3431   Note:
3432   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3433 
3434 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3435 @*/
3436 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3437 {
3438   DM_Plex *mesh = (DM_Plex *)dm->data;
3439   PetscInt pStart, pEnd;
3440   PetscInt dof, off, c;
3441 
3442   PetscFunctionBegin;
3443   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3444   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3445   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3446   if (dof) PetscValidIntPointer(support, 3);
3447   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3448   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);
3449   for (c = 0; c < dof; ++c) {
3450     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);
3451     mesh->supports[off + c] = support[c];
3452   }
3453   PetscFunctionReturn(0);
3454 }
3455 
3456 /*@
3457   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3458 
3459   Not Collective
3460 
3461   Input Parameters:
3462 + mesh - The `DMPLEX`
3463 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3464 . supportPos - The local index in the cone where the point should be put
3465 - supportPoint - The mesh point to insert
3466 
3467   Level: beginner
3468 
3469 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3470 @*/
3471 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3472 {
3473   DM_Plex *mesh = (DM_Plex *)dm->data;
3474   PetscInt pStart, pEnd;
3475   PetscInt dof, off;
3476 
3477   PetscFunctionBegin;
3478   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3479   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3480   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3481   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3482   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);
3483   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);
3484   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);
3485   mesh->supports[off + supportPos] = supportPoint;
3486   PetscFunctionReturn(0);
3487 }
3488 
3489 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3490 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3491 {
3492   switch (ct) {
3493   case DM_POLYTOPE_SEGMENT:
3494     if (o == -1) return -2;
3495     break;
3496   case DM_POLYTOPE_TRIANGLE:
3497     if (o == -3) return -1;
3498     if (o == -2) return -3;
3499     if (o == -1) return -2;
3500     break;
3501   case DM_POLYTOPE_QUADRILATERAL:
3502     if (o == -4) return -2;
3503     if (o == -3) return -1;
3504     if (o == -2) return -4;
3505     if (o == -1) return -3;
3506     break;
3507   default:
3508     return o;
3509   }
3510   return o;
3511 }
3512 
3513 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3514 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3515 {
3516   switch (ct) {
3517   case DM_POLYTOPE_SEGMENT:
3518     if ((o == -2) || (o == 1)) return -1;
3519     if (o == -1) return 0;
3520     break;
3521   case DM_POLYTOPE_TRIANGLE:
3522     if (o == -3) return -2;
3523     if (o == -2) return -1;
3524     if (o == -1) return -3;
3525     break;
3526   case DM_POLYTOPE_QUADRILATERAL:
3527     if (o == -4) return -2;
3528     if (o == -3) return -1;
3529     if (o == -2) return -4;
3530     if (o == -1) return -3;
3531     break;
3532   default:
3533     return o;
3534   }
3535   return o;
3536 }
3537 
3538 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3539 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3540 {
3541   PetscInt pStart, pEnd, p;
3542 
3543   PetscFunctionBegin;
3544   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3545   for (p = pStart; p < pEnd; ++p) {
3546     const PetscInt *cone, *ornt;
3547     PetscInt        coneSize, c;
3548 
3549     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3550     PetscCall(DMPlexGetCone(dm, p, &cone));
3551     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3552     for (c = 0; c < coneSize; ++c) {
3553       DMPolytopeType ct;
3554       const PetscInt o = ornt[c];
3555 
3556       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3557       switch (ct) {
3558       case DM_POLYTOPE_SEGMENT:
3559         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3560         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3561         break;
3562       case DM_POLYTOPE_TRIANGLE:
3563         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3564         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3565         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3566         break;
3567       case DM_POLYTOPE_QUADRILATERAL:
3568         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3569         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3570         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3571         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3572         break;
3573       default:
3574         break;
3575       }
3576     }
3577   }
3578   PetscFunctionReturn(0);
3579 }
3580 
3581 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3582 {
3583   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3584   PetscInt       *closure;
3585   const PetscInt *tmp = NULL, *tmpO = NULL;
3586   PetscInt        off = 0, tmpSize, t;
3587 
3588   PetscFunctionBeginHot;
3589   if (ornt) {
3590     PetscCall(DMPlexGetCellType(dm, p, &ct));
3591     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3592   }
3593   if (*points) {
3594     closure = *points;
3595   } else {
3596     PetscInt maxConeSize, maxSupportSize;
3597     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3598     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3599   }
3600   if (useCone) {
3601     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3602     PetscCall(DMPlexGetCone(dm, p, &tmp));
3603     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3604   } else {
3605     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3606     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3607   }
3608   if (ct == DM_POLYTOPE_UNKNOWN) {
3609     closure[off++] = p;
3610     closure[off++] = 0;
3611     for (t = 0; t < tmpSize; ++t) {
3612       closure[off++] = tmp[t];
3613       closure[off++] = tmpO ? tmpO[t] : 0;
3614     }
3615   } else {
3616     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3617 
3618     /* We assume that cells with a valid type have faces with a valid type */
3619     closure[off++] = p;
3620     closure[off++] = ornt;
3621     for (t = 0; t < tmpSize; ++t) {
3622       DMPolytopeType ft;
3623 
3624       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3625       closure[off++] = tmp[arr[t]];
3626       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3627     }
3628   }
3629   if (numPoints) *numPoints = tmpSize + 1;
3630   if (points) *points = closure;
3631   PetscFunctionReturn(0);
3632 }
3633 
3634 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3635 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3636 {
3637   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3638   const PetscInt *cone, *ornt;
3639   PetscInt       *pts, *closure = NULL;
3640   DMPolytopeType  ft;
3641   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3642   PetscInt        dim, coneSize, c, d, clSize, cl;
3643 
3644   PetscFunctionBeginHot;
3645   PetscCall(DMGetDimension(dm, &dim));
3646   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3647   PetscCall(DMPlexGetCone(dm, point, &cone));
3648   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3649   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3650   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3651   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3652   maxSize       = PetscMax(coneSeries, supportSeries);
3653   if (*points) {
3654     pts = *points;
3655   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3656   c        = 0;
3657   pts[c++] = point;
3658   pts[c++] = o;
3659   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3660   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3661   for (cl = 0; cl < clSize * 2; cl += 2) {
3662     pts[c++] = closure[cl];
3663     pts[c++] = closure[cl + 1];
3664   }
3665   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3666   for (cl = 0; cl < clSize * 2; cl += 2) {
3667     pts[c++] = closure[cl];
3668     pts[c++] = closure[cl + 1];
3669   }
3670   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3671   for (d = 2; d < coneSize; ++d) {
3672     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3673     pts[c++] = cone[arr[d * 2 + 0]];
3674     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3675   }
3676   if (dim >= 3) {
3677     for (d = 2; d < coneSize; ++d) {
3678       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3679       const PetscInt *fcone, *fornt;
3680       PetscInt        fconeSize, fc, i;
3681 
3682       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3683       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3684       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3685       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3686       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3687       for (fc = 0; fc < fconeSize; ++fc) {
3688         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3689         const PetscInt co = farr[fc * 2 + 1];
3690 
3691         for (i = 0; i < c; i += 2)
3692           if (pts[i] == cp) break;
3693         if (i == c) {
3694           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3695           pts[c++] = cp;
3696           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3697         }
3698       }
3699     }
3700   }
3701   *numPoints = c / 2;
3702   *points    = pts;
3703   PetscFunctionReturn(0);
3704 }
3705 
3706 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3707 {
3708   DMPolytopeType ct;
3709   PetscInt      *closure, *fifo;
3710   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3711   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3712   PetscInt       depth, maxSize;
3713 
3714   PetscFunctionBeginHot;
3715   PetscCall(DMPlexGetDepth(dm, &depth));
3716   if (depth == 1) {
3717     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3718     PetscFunctionReturn(0);
3719   }
3720   PetscCall(DMPlexGetCellType(dm, p, &ct));
3721   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3722   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3723     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3724     PetscFunctionReturn(0);
3725   }
3726   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3727   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3728   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3729   maxSize       = PetscMax(coneSeries, supportSeries);
3730   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3731   if (*points) {
3732     closure = *points;
3733   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3734   closure[closureSize++] = p;
3735   closure[closureSize++] = ornt;
3736   fifo[fifoSize++]       = p;
3737   fifo[fifoSize++]       = ornt;
3738   fifo[fifoSize++]       = ct;
3739   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3740   while (fifoSize - fifoStart) {
3741     const PetscInt       q    = fifo[fifoStart++];
3742     const PetscInt       o    = fifo[fifoStart++];
3743     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3744     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3745     const PetscInt      *tmp, *tmpO;
3746     PetscInt             tmpSize, t;
3747 
3748     if (PetscDefined(USE_DEBUG)) {
3749       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3750       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);
3751     }
3752     if (useCone) {
3753       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3754       PetscCall(DMPlexGetCone(dm, q, &tmp));
3755       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3756     } else {
3757       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3758       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3759       tmpO = NULL;
3760     }
3761     for (t = 0; t < tmpSize; ++t) {
3762       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3763       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3764       const PetscInt cp = tmp[ip];
3765       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3766       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3767       PetscInt       c;
3768 
3769       /* Check for duplicate */
3770       for (c = 0; c < closureSize; c += 2) {
3771         if (closure[c] == cp) break;
3772       }
3773       if (c == closureSize) {
3774         closure[closureSize++] = cp;
3775         closure[closureSize++] = co;
3776         fifo[fifoSize++]       = cp;
3777         fifo[fifoSize++]       = co;
3778         fifo[fifoSize++]       = ct;
3779       }
3780     }
3781   }
3782   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3783   if (numPoints) *numPoints = closureSize / 2;
3784   if (points) *points = closure;
3785   PetscFunctionReturn(0);
3786 }
3787 
3788 /*@C
3789   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3790 
3791   Not Collective
3792 
3793   Input Parameters:
3794 + dm      - The `DMPLEX`
3795 . p       - The mesh point
3796 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3797 
3798   Input/Output Parameter:
3799 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3800            if NULL on input, internal storage will be returned, otherwise the provided array is used
3801 
3802   Output Parameter:
3803 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3804 
3805   Level: beginner
3806 
3807   Note:
3808   If using internal storage (points is NULL on input), each call overwrites the last output.
3809 
3810   Fortran Note:
3811   The numPoints argument is not present in the Fortran binding since it is internal to the array.
3812 
3813 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3814 @*/
3815 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3816 {
3817   PetscFunctionBeginHot;
3818   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3819   if (numPoints) PetscValidIntPointer(numPoints, 4);
3820   if (points) PetscValidPointer(points, 5);
3821   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3822   PetscFunctionReturn(0);
3823 }
3824 
3825 /*@C
3826   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3827 
3828   Not Collective
3829 
3830   Input Parameters:
3831 + dm        - The `DMPLEX`
3832 . p         - The mesh point
3833 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3834 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3835 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3836 
3837   Level: beginner
3838 
3839   Note:
3840   If not using internal storage (points is not NULL on input), this call is unnecessary
3841 
3842 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3843 @*/
3844 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3845 {
3846   PetscFunctionBeginHot;
3847   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3848   if (numPoints) *numPoints = 0;
3849   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3850   PetscFunctionReturn(0);
3851 }
3852 
3853 /*@
3854   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3855 
3856   Not Collective
3857 
3858   Input Parameter:
3859 . mesh - The `DMPLEX`
3860 
3861   Output Parameters:
3862 + maxConeSize - The maximum number of in-edges
3863 - maxSupportSize - The maximum number of out-edges
3864 
3865   Level: beginner
3866 
3867 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3868 @*/
3869 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3870 {
3871   DM_Plex *mesh = (DM_Plex *)dm->data;
3872 
3873   PetscFunctionBegin;
3874   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3875   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3876   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3877   PetscFunctionReturn(0);
3878 }
3879 
3880 PetscErrorCode DMSetUp_Plex(DM dm)
3881 {
3882   DM_Plex *mesh = (DM_Plex *)dm->data;
3883   PetscInt size, maxSupportSize;
3884 
3885   PetscFunctionBegin;
3886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3887   PetscCall(PetscSectionSetUp(mesh->coneSection));
3888   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3889   PetscCall(PetscMalloc1(size, &mesh->cones));
3890   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3891   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3892   if (maxSupportSize) {
3893     PetscCall(PetscSectionSetUp(mesh->supportSection));
3894     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3895     PetscCall(PetscMalloc1(size, &mesh->supports));
3896   }
3897   PetscFunctionReturn(0);
3898 }
3899 
3900 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3901 {
3902   PetscFunctionBegin;
3903   if (subdm) PetscCall(DMClone(dm, subdm));
3904   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3905   if (subdm) (*subdm)->useNatural = dm->useNatural;
3906   if (dm->useNatural && dm->sfMigration) {
3907     PetscSF sfNatural;
3908 
3909     (*subdm)->sfMigration = dm->sfMigration;
3910     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3911     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3912     (*subdm)->sfNatural = sfNatural;
3913   }
3914   PetscFunctionReturn(0);
3915 }
3916 
3917 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3918 {
3919   PetscInt i = 0;
3920 
3921   PetscFunctionBegin;
3922   PetscCall(DMClone(dms[0], superdm));
3923   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3924   (*superdm)->useNatural = PETSC_FALSE;
3925   for (i = 0; i < len; i++) {
3926     if (dms[i]->useNatural && dms[i]->sfMigration) {
3927       PetscSF sfNatural;
3928 
3929       (*superdm)->sfMigration = dms[i]->sfMigration;
3930       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3931       (*superdm)->useNatural = PETSC_TRUE;
3932       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3933       (*superdm)->sfNatural = sfNatural;
3934       break;
3935     }
3936   }
3937   PetscFunctionReturn(0);
3938 }
3939 
3940 /*@
3941   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3942 
3943   Not Collective
3944 
3945   Input Parameter:
3946 . mesh - The `DMPLEX`
3947 
3948   Level: beginner
3949 
3950   Note:
3951   This should be called after all calls to `DMPlexSetCone()`
3952 
3953 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3954 @*/
3955 PetscErrorCode DMPlexSymmetrize(DM dm)
3956 {
3957   DM_Plex  *mesh = (DM_Plex *)dm->data;
3958   PetscInt *offsets;
3959   PetscInt  supportSize;
3960   PetscInt  pStart, pEnd, p;
3961 
3962   PetscFunctionBegin;
3963   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3964   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3965   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3966   /* Calculate support sizes */
3967   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3968   for (p = pStart; p < pEnd; ++p) {
3969     PetscInt dof, off, c;
3970 
3971     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3972     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3973     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3974   }
3975   PetscCall(PetscSectionSetUp(mesh->supportSection));
3976   /* Calculate supports */
3977   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3978   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3979   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
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) {
3986       const PetscInt q = mesh->cones[c];
3987       PetscInt       offS;
3988 
3989       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3990 
3991       mesh->supports[offS + offsets[q]] = p;
3992       ++offsets[q];
3993     }
3994   }
3995   PetscCall(PetscFree(offsets));
3996   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
3997   PetscFunctionReturn(0);
3998 }
3999 
4000 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4001 {
4002   IS stratumIS;
4003 
4004   PetscFunctionBegin;
4005   if (pStart >= pEnd) PetscFunctionReturn(0);
4006   if (PetscDefined(USE_DEBUG)) {
4007     PetscInt  qStart, qEnd, numLevels, level;
4008     PetscBool overlap = PETSC_FALSE;
4009     PetscCall(DMLabelGetNumValues(label, &numLevels));
4010     for (level = 0; level < numLevels; level++) {
4011       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4012       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4013         overlap = PETSC_TRUE;
4014         break;
4015       }
4016     }
4017     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);
4018   }
4019   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4020   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4021   PetscCall(ISDestroy(&stratumIS));
4022   PetscFunctionReturn(0);
4023 }
4024 
4025 /*@
4026   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4027   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4028   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4029   the DAG.
4030 
4031   Collective on dm
4032 
4033   Input Parameter:
4034 . mesh - The `DMPLEX`
4035 
4036   Level: beginner
4037 
4038   Notes:
4039   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4040   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4041   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4042   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4043   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4044 
4045   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4046   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4047   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
4048   to interpolate only that one (e0), so that
4049 .vb
4050   cone(c0) = {e0, v2}
4051   cone(e0) = {v0, v1}
4052 .ve
4053   If `DMPlexStratify()` is run on this mesh, it will give depths
4054 .vb
4055    depth 0 = {v0, v1, v2}
4056    depth 1 = {e0, c0}
4057 .ve
4058   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4059 
4060   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4061 
4062 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4063 @*/
4064 PetscErrorCode DMPlexStratify(DM dm)
4065 {
4066   DM_Plex *mesh = (DM_Plex *)dm->data;
4067   DMLabel  label;
4068   PetscInt pStart, pEnd, p;
4069   PetscInt numRoots = 0, numLeaves = 0;
4070 
4071   PetscFunctionBegin;
4072   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4073   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4074 
4075   /* Create depth label */
4076   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4077   PetscCall(DMCreateLabel(dm, "depth"));
4078   PetscCall(DMPlexGetDepthLabel(dm, &label));
4079 
4080   {
4081     /* Initialize roots and count leaves */
4082     PetscInt sMin = PETSC_MAX_INT;
4083     PetscInt sMax = PETSC_MIN_INT;
4084     PetscInt coneSize, supportSize;
4085 
4086     for (p = pStart; p < pEnd; ++p) {
4087       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4088       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4089       if (!coneSize && supportSize) {
4090         sMin = PetscMin(p, sMin);
4091         sMax = PetscMax(p, sMax);
4092         ++numRoots;
4093       } else if (!supportSize && coneSize) {
4094         ++numLeaves;
4095       } else if (!supportSize && !coneSize) {
4096         /* Isolated points */
4097         sMin = PetscMin(p, sMin);
4098         sMax = PetscMax(p, sMax);
4099       }
4100     }
4101     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4102   }
4103 
4104   if (numRoots + numLeaves == (pEnd - pStart)) {
4105     PetscInt sMin = PETSC_MAX_INT;
4106     PetscInt sMax = PETSC_MIN_INT;
4107     PetscInt coneSize, supportSize;
4108 
4109     for (p = pStart; p < pEnd; ++p) {
4110       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4111       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4112       if (!supportSize && coneSize) {
4113         sMin = PetscMin(p, sMin);
4114         sMax = PetscMax(p, sMax);
4115       }
4116     }
4117     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4118   } else {
4119     PetscInt level = 0;
4120     PetscInt qStart, qEnd, q;
4121 
4122     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4123     while (qEnd > qStart) {
4124       PetscInt sMin = PETSC_MAX_INT;
4125       PetscInt sMax = PETSC_MIN_INT;
4126 
4127       for (q = qStart; q < qEnd; ++q) {
4128         const PetscInt *support;
4129         PetscInt        supportSize, s;
4130 
4131         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4132         PetscCall(DMPlexGetSupport(dm, q, &support));
4133         for (s = 0; s < supportSize; ++s) {
4134           sMin = PetscMin(support[s], sMin);
4135           sMax = PetscMax(support[s], sMax);
4136         }
4137       }
4138       PetscCall(DMLabelGetNumValues(label, &level));
4139       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4140       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4141     }
4142   }
4143   { /* just in case there is an empty process */
4144     PetscInt numValues, maxValues = 0, v;
4145 
4146     PetscCall(DMLabelGetNumValues(label, &numValues));
4147     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4148     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4149   }
4150   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4151   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4152   PetscFunctionReturn(0);
4153 }
4154 
4155 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4156 {
4157   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4158   PetscInt       dim, depth, pheight, coneSize;
4159 
4160   PetscFunctionBeginHot;
4161   PetscCall(DMGetDimension(dm, &dim));
4162   PetscCall(DMPlexGetDepth(dm, &depth));
4163   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4164   pheight = depth - pdepth;
4165   if (depth <= 1) {
4166     switch (pdepth) {
4167     case 0:
4168       ct = DM_POLYTOPE_POINT;
4169       break;
4170     case 1:
4171       switch (coneSize) {
4172       case 2:
4173         ct = DM_POLYTOPE_SEGMENT;
4174         break;
4175       case 3:
4176         ct = DM_POLYTOPE_TRIANGLE;
4177         break;
4178       case 4:
4179         switch (dim) {
4180         case 2:
4181           ct = DM_POLYTOPE_QUADRILATERAL;
4182           break;
4183         case 3:
4184           ct = DM_POLYTOPE_TETRAHEDRON;
4185           break;
4186         default:
4187           break;
4188         }
4189         break;
4190       case 5:
4191         ct = DM_POLYTOPE_PYRAMID;
4192         break;
4193       case 6:
4194         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4195         break;
4196       case 8:
4197         ct = DM_POLYTOPE_HEXAHEDRON;
4198         break;
4199       default:
4200         break;
4201       }
4202     }
4203   } else {
4204     if (pdepth == 0) {
4205       ct = DM_POLYTOPE_POINT;
4206     } else if (pheight == 0) {
4207       switch (dim) {
4208       case 1:
4209         switch (coneSize) {
4210         case 2:
4211           ct = DM_POLYTOPE_SEGMENT;
4212           break;
4213         default:
4214           break;
4215         }
4216         break;
4217       case 2:
4218         switch (coneSize) {
4219         case 3:
4220           ct = DM_POLYTOPE_TRIANGLE;
4221           break;
4222         case 4:
4223           ct = DM_POLYTOPE_QUADRILATERAL;
4224           break;
4225         default:
4226           break;
4227         }
4228         break;
4229       case 3:
4230         switch (coneSize) {
4231         case 4:
4232           ct = DM_POLYTOPE_TETRAHEDRON;
4233           break;
4234         case 5: {
4235           const PetscInt *cone;
4236           PetscInt        faceConeSize;
4237 
4238           PetscCall(DMPlexGetCone(dm, p, &cone));
4239           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4240           switch (faceConeSize) {
4241           case 3:
4242             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4243             break;
4244           case 4:
4245             ct = DM_POLYTOPE_PYRAMID;
4246             break;
4247           }
4248         } break;
4249         case 6:
4250           ct = DM_POLYTOPE_HEXAHEDRON;
4251           break;
4252         default:
4253           break;
4254         }
4255         break;
4256       default:
4257         break;
4258       }
4259     } else if (pheight > 0) {
4260       switch (coneSize) {
4261       case 2:
4262         ct = DM_POLYTOPE_SEGMENT;
4263         break;
4264       case 3:
4265         ct = DM_POLYTOPE_TRIANGLE;
4266         break;
4267       case 4:
4268         ct = DM_POLYTOPE_QUADRILATERAL;
4269         break;
4270       default:
4271         break;
4272       }
4273     }
4274   }
4275   *pt = ct;
4276   PetscFunctionReturn(0);
4277 }
4278 
4279 /*@
4280   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4281 
4282   Collective on dm
4283 
4284   Input Parameter:
4285 . mesh - The `DMPLEX`
4286 
4287   Level: developer
4288 
4289   Note:
4290   This function is normally called automatically when a cell type is requested. It creates an
4291   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4292   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4293 
4294   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4295 
4296 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4297 @*/
4298 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4299 {
4300   DM_Plex *mesh;
4301   DMLabel  ctLabel;
4302   PetscInt pStart, pEnd, p;
4303 
4304   PetscFunctionBegin;
4305   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4306   mesh = (DM_Plex *)dm->data;
4307   PetscCall(DMCreateLabel(dm, "celltype"));
4308   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4309   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4310   for (p = pStart; p < pEnd; ++p) {
4311     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4312     PetscInt       pdepth;
4313 
4314     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4315     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4316     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4317     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4318   }
4319   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4320   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4321   PetscFunctionReturn(0);
4322 }
4323 
4324 /*@C
4325   DMPlexGetJoin - Get an array for the join of the set of points
4326 
4327   Not Collective
4328 
4329   Input Parameters:
4330 + dm - The `DMPLEX` object
4331 . numPoints - The number of input points for the join
4332 - points - The input points
4333 
4334   Output Parameters:
4335 + numCoveredPoints - The number of points in the join
4336 - coveredPoints - The points in the join
4337 
4338   Level: intermediate
4339 
4340   Note:
4341   Currently, this is restricted to a single level join
4342 
4343   Fortran Note:
4344   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4345 
4346 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4347 @*/
4348 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4349 {
4350   DM_Plex  *mesh = (DM_Plex *)dm->data;
4351   PetscInt *join[2];
4352   PetscInt  joinSize, i = 0;
4353   PetscInt  dof, off, p, c, m;
4354   PetscInt  maxSupportSize;
4355 
4356   PetscFunctionBegin;
4357   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4358   PetscValidIntPointer(points, 3);
4359   PetscValidIntPointer(numCoveredPoints, 4);
4360   PetscValidPointer(coveredPoints, 5);
4361   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4362   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4363   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4364   /* Copy in support of first point */
4365   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4366   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4367   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4368   /* Check each successive support */
4369   for (p = 1; p < numPoints; ++p) {
4370     PetscInt newJoinSize = 0;
4371 
4372     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4373     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4374     for (c = 0; c < dof; ++c) {
4375       const PetscInt point = mesh->supports[off + c];
4376 
4377       for (m = 0; m < joinSize; ++m) {
4378         if (point == join[i][m]) {
4379           join[1 - i][newJoinSize++] = point;
4380           break;
4381         }
4382       }
4383     }
4384     joinSize = newJoinSize;
4385     i        = 1 - i;
4386   }
4387   *numCoveredPoints = joinSize;
4388   *coveredPoints    = join[i];
4389   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4390   PetscFunctionReturn(0);
4391 }
4392 
4393 /*@C
4394   DMPlexRestoreJoin - Restore an array for the join of the set of points
4395 
4396   Not Collective
4397 
4398   Input Parameters:
4399 + dm - The `DMPLEX` object
4400 . numPoints - The number of input points for the join
4401 - points - The input points
4402 
4403   Output Parameters:
4404 + numCoveredPoints - The number of points in the join
4405 - coveredPoints - The points in the join
4406 
4407   Level: intermediate
4408 
4409   Fortran Note:
4410   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4411 
4412 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4413 @*/
4414 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4415 {
4416   PetscFunctionBegin;
4417   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4418   if (points) PetscValidIntPointer(points, 3);
4419   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4420   PetscValidPointer(coveredPoints, 5);
4421   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4422   if (numCoveredPoints) *numCoveredPoints = 0;
4423   PetscFunctionReturn(0);
4424 }
4425 
4426 /*@C
4427   DMPlexGetFullJoin - Get an array for the join of the set of points
4428 
4429   Not Collective
4430 
4431   Input Parameters:
4432 + dm - The `DMPLEX` object
4433 . numPoints - The number of input points for the join
4434 - points - The input points
4435 
4436   Output Parameters:
4437 + numCoveredPoints - The number of points in the join
4438 - coveredPoints - The points in the join
4439 
4440   Level: intermediate
4441 
4442   Fortran Note:
4443   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4444 
4445 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4446 @*/
4447 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4448 {
4449   PetscInt *offsets, **closures;
4450   PetscInt *join[2];
4451   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4452   PetscInt  p, d, c, m, ms;
4453 
4454   PetscFunctionBegin;
4455   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4456   PetscValidIntPointer(points, 3);
4457   PetscValidIntPointer(numCoveredPoints, 4);
4458   PetscValidPointer(coveredPoints, 5);
4459 
4460   PetscCall(DMPlexGetDepth(dm, &depth));
4461   PetscCall(PetscCalloc1(numPoints, &closures));
4462   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4463   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4464   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4465   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4466   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4467 
4468   for (p = 0; p < numPoints; ++p) {
4469     PetscInt closureSize;
4470 
4471     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4472 
4473     offsets[p * (depth + 2) + 0] = 0;
4474     for (d = 0; d < depth + 1; ++d) {
4475       PetscInt pStart, pEnd, i;
4476 
4477       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4478       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4479         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4480           offsets[p * (depth + 2) + d + 1] = i;
4481           break;
4482         }
4483       }
4484       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4485     }
4486     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);
4487   }
4488   for (d = 0; d < depth + 1; ++d) {
4489     PetscInt dof;
4490 
4491     /* Copy in support of first point */
4492     dof = offsets[d + 1] - offsets[d];
4493     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4494     /* Check each successive cone */
4495     for (p = 1; p < numPoints && joinSize; ++p) {
4496       PetscInt newJoinSize = 0;
4497 
4498       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4499       for (c = 0; c < dof; ++c) {
4500         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4501 
4502         for (m = 0; m < joinSize; ++m) {
4503           if (point == join[i][m]) {
4504             join[1 - i][newJoinSize++] = point;
4505             break;
4506           }
4507         }
4508       }
4509       joinSize = newJoinSize;
4510       i        = 1 - i;
4511     }
4512     if (joinSize) break;
4513   }
4514   *numCoveredPoints = joinSize;
4515   *coveredPoints    = join[i];
4516   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4517   PetscCall(PetscFree(closures));
4518   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4519   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4520   PetscFunctionReturn(0);
4521 }
4522 
4523 /*@C
4524   DMPlexGetMeet - Get an array for the meet of the set of points
4525 
4526   Not Collective
4527 
4528   Input Parameters:
4529 + dm - The `DMPLEX` object
4530 . numPoints - The number of input points for the meet
4531 - points - The input points
4532 
4533   Output Parameters:
4534 + numCoveredPoints - The number of points in the meet
4535 - coveredPoints - The points in the meet
4536 
4537   Level: intermediate
4538 
4539   Note:
4540   Currently, this is restricted to a single level meet
4541 
4542   Fortran Notes:
4543   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4544 
4545 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4546 @*/
4547 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4548 {
4549   DM_Plex  *mesh = (DM_Plex *)dm->data;
4550   PetscInt *meet[2];
4551   PetscInt  meetSize, i = 0;
4552   PetscInt  dof, off, p, c, m;
4553   PetscInt  maxConeSize;
4554 
4555   PetscFunctionBegin;
4556   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4557   PetscValidIntPointer(points, 3);
4558   PetscValidIntPointer(numCoveringPoints, 4);
4559   PetscValidPointer(coveringPoints, 5);
4560   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4561   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4562   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4563   /* Copy in cone of first point */
4564   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4565   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4566   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4567   /* Check each successive cone */
4568   for (p = 1; p < numPoints; ++p) {
4569     PetscInt newMeetSize = 0;
4570 
4571     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4572     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4573     for (c = 0; c < dof; ++c) {
4574       const PetscInt point = mesh->cones[off + c];
4575 
4576       for (m = 0; m < meetSize; ++m) {
4577         if (point == meet[i][m]) {
4578           meet[1 - i][newMeetSize++] = point;
4579           break;
4580         }
4581       }
4582     }
4583     meetSize = newMeetSize;
4584     i        = 1 - i;
4585   }
4586   *numCoveringPoints = meetSize;
4587   *coveringPoints    = meet[i];
4588   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4589   PetscFunctionReturn(0);
4590 }
4591 
4592 /*@C
4593   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4594 
4595   Not Collective
4596 
4597   Input Parameters:
4598 + dm - The `DMPLEX` object
4599 . numPoints - The number of input points for the meet
4600 - points - The input points
4601 
4602   Output Parameters:
4603 + numCoveredPoints - The number of points in the meet
4604 - coveredPoints - The points in the meet
4605 
4606   Level: intermediate
4607 
4608   Fortran Note:
4609   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4610 
4611 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4612 @*/
4613 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4614 {
4615   PetscFunctionBegin;
4616   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4617   if (points) PetscValidIntPointer(points, 3);
4618   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4619   PetscValidPointer(coveredPoints, 5);
4620   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4621   if (numCoveredPoints) *numCoveredPoints = 0;
4622   PetscFunctionReturn(0);
4623 }
4624 
4625 /*@C
4626   DMPlexGetFullMeet - Get an array for the meet of the set of points
4627 
4628   Not Collective
4629 
4630   Input Parameters:
4631 + dm - The `DMPLEX` object
4632 . numPoints - The number of input points for the meet
4633 - points - The input points
4634 
4635   Output Parameters:
4636 + numCoveredPoints - The number of points in the meet
4637 - coveredPoints - The points in the meet
4638 
4639   Level: intermediate
4640 
4641   Fortran Note:
4642   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4643 
4644 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4645 @*/
4646 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4647 {
4648   PetscInt *offsets, **closures;
4649   PetscInt *meet[2];
4650   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4651   PetscInt  p, h, c, m, mc;
4652 
4653   PetscFunctionBegin;
4654   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4655   PetscValidIntPointer(points, 3);
4656   PetscValidIntPointer(numCoveredPoints, 4);
4657   PetscValidPointer(coveredPoints, 5);
4658 
4659   PetscCall(DMPlexGetDepth(dm, &height));
4660   PetscCall(PetscMalloc1(numPoints, &closures));
4661   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4662   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4663   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4664   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4665   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4666 
4667   for (p = 0; p < numPoints; ++p) {
4668     PetscInt closureSize;
4669 
4670     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4671 
4672     offsets[p * (height + 2) + 0] = 0;
4673     for (h = 0; h < height + 1; ++h) {
4674       PetscInt pStart, pEnd, i;
4675 
4676       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4677       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4678         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4679           offsets[p * (height + 2) + h + 1] = i;
4680           break;
4681         }
4682       }
4683       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4684     }
4685     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);
4686   }
4687   for (h = 0; h < height + 1; ++h) {
4688     PetscInt dof;
4689 
4690     /* Copy in cone of first point */
4691     dof = offsets[h + 1] - offsets[h];
4692     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4693     /* Check each successive cone */
4694     for (p = 1; p < numPoints && meetSize; ++p) {
4695       PetscInt newMeetSize = 0;
4696 
4697       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4698       for (c = 0; c < dof; ++c) {
4699         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4700 
4701         for (m = 0; m < meetSize; ++m) {
4702           if (point == meet[i][m]) {
4703             meet[1 - i][newMeetSize++] = point;
4704             break;
4705           }
4706         }
4707       }
4708       meetSize = newMeetSize;
4709       i        = 1 - i;
4710     }
4711     if (meetSize) break;
4712   }
4713   *numCoveredPoints = meetSize;
4714   *coveredPoints    = meet[i];
4715   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4716   PetscCall(PetscFree(closures));
4717   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4718   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4719   PetscFunctionReturn(0);
4720 }
4721 
4722 /*@C
4723   DMPlexEqual - Determine if two `DM` have the same topology
4724 
4725   Not Collective
4726 
4727   Input Parameters:
4728 + dmA - A `DMPLEX` object
4729 - dmB - A `DMPLEX` object
4730 
4731   Output Parameters:
4732 . equal - `PETSC_TRUE` if the topologies are identical
4733 
4734   Level: intermediate
4735 
4736   Note:
4737   We are not solving graph isomorphism, so we do not permute.
4738 
4739 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4740 @*/
4741 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4742 {
4743   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4744 
4745   PetscFunctionBegin;
4746   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4747   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4748   PetscValidBoolPointer(equal, 3);
4749 
4750   *equal = PETSC_FALSE;
4751   PetscCall(DMPlexGetDepth(dmA, &depth));
4752   PetscCall(DMPlexGetDepth(dmB, &depthB));
4753   if (depth != depthB) PetscFunctionReturn(0);
4754   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4755   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4756   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4757   for (p = pStart; p < pEnd; ++p) {
4758     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4759     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4760 
4761     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4762     PetscCall(DMPlexGetCone(dmA, p, &cone));
4763     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4764     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4765     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4766     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4767     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4768     for (c = 0; c < coneSize; ++c) {
4769       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4770       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4771     }
4772     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4773     PetscCall(DMPlexGetSupport(dmA, p, &support));
4774     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4775     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4776     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4777     for (s = 0; s < supportSize; ++s) {
4778       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4779     }
4780   }
4781   *equal = PETSC_TRUE;
4782   PetscFunctionReturn(0);
4783 }
4784 
4785 /*@C
4786   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4787 
4788   Not Collective
4789 
4790   Input Parameters:
4791 + dm         - The `DMPLEX`
4792 . cellDim    - The cell dimension
4793 - numCorners - The number of vertices on a cell
4794 
4795   Output Parameters:
4796 . numFaceVertices - The number of vertices on a face
4797 
4798   Level: developer
4799 
4800   Note:
4801   Of course this can only work for a restricted set of symmetric shapes
4802 
4803 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4804 @*/
4805 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4806 {
4807   MPI_Comm comm;
4808 
4809   PetscFunctionBegin;
4810   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4811   PetscValidIntPointer(numFaceVertices, 4);
4812   switch (cellDim) {
4813   case 0:
4814     *numFaceVertices = 0;
4815     break;
4816   case 1:
4817     *numFaceVertices = 1;
4818     break;
4819   case 2:
4820     switch (numCorners) {
4821     case 3:                 /* triangle */
4822       *numFaceVertices = 2; /* Edge has 2 vertices */
4823       break;
4824     case 4:                 /* quadrilateral */
4825       *numFaceVertices = 2; /* Edge has 2 vertices */
4826       break;
4827     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4828       *numFaceVertices = 3; /* Edge has 3 vertices */
4829       break;
4830     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4831       *numFaceVertices = 3; /* Edge has 3 vertices */
4832       break;
4833     default:
4834       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4835     }
4836     break;
4837   case 3:
4838     switch (numCorners) {
4839     case 4:                 /* tetradehdron */
4840       *numFaceVertices = 3; /* Face has 3 vertices */
4841       break;
4842     case 6:                 /* tet cohesive cells */
4843       *numFaceVertices = 4; /* Face has 4 vertices */
4844       break;
4845     case 8:                 /* hexahedron */
4846       *numFaceVertices = 4; /* Face has 4 vertices */
4847       break;
4848     case 9:                 /* tet cohesive Lagrange cells */
4849       *numFaceVertices = 6; /* Face has 6 vertices */
4850       break;
4851     case 10:                /* quadratic tetrahedron */
4852       *numFaceVertices = 6; /* Face has 6 vertices */
4853       break;
4854     case 12:                /* hex cohesive Lagrange cells */
4855       *numFaceVertices = 6; /* Face has 6 vertices */
4856       break;
4857     case 18:                /* quadratic tet cohesive Lagrange cells */
4858       *numFaceVertices = 6; /* Face has 6 vertices */
4859       break;
4860     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4861       *numFaceVertices = 9; /* Face has 9 vertices */
4862       break;
4863     default:
4864       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4865     }
4866     break;
4867   default:
4868     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4869   }
4870   PetscFunctionReturn(0);
4871 }
4872 
4873 /*@
4874   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4875 
4876   Not Collective
4877 
4878   Input Parameter:
4879 . dm    - The `DMPLEX` object
4880 
4881   Output Parameter:
4882 . depthLabel - The `DMLabel` recording point depth
4883 
4884   Level: developer
4885 
4886 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4887 @*/
4888 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4889 {
4890   PetscFunctionBegin;
4891   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4892   PetscValidPointer(depthLabel, 2);
4893   *depthLabel = dm->depthLabel;
4894   PetscFunctionReturn(0);
4895 }
4896 
4897 /*@
4898   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4899 
4900   Not Collective
4901 
4902   Input Parameter:
4903 . dm    - The `DMPLEX` object
4904 
4905   Output Parameter:
4906 . depth - The number of strata (breadth first levels) in the DAG
4907 
4908   Level: developer
4909 
4910   Notes:
4911   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4912 
4913   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4914 
4915   An empty mesh gives -1.
4916 
4917 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4918 @*/
4919 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4920 {
4921   DM_Plex *mesh = (DM_Plex *)dm->data;
4922   DMLabel  label;
4923   PetscInt d = 0;
4924 
4925   PetscFunctionBegin;
4926   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4927   PetscValidIntPointer(depth, 2);
4928   if (mesh->tr) {
4929     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4930   } else {
4931     PetscCall(DMPlexGetDepthLabel(dm, &label));
4932     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4933     *depth = d - 1;
4934   }
4935   PetscFunctionReturn(0);
4936 }
4937 
4938 /*@
4939   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4940 
4941   Not Collective
4942 
4943   Input Parameters:
4944 + dm    - The `DMPLEX` object
4945 - depth - The requested depth
4946 
4947   Output Parameters:
4948 + start - The first point at this depth
4949 - end   - One beyond the last point at this depth
4950 
4951   Level: developer
4952 
4953   Notes:
4954   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4955   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
4956   higher dimension, e.g., "edges".
4957 
4958 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4959 @*/
4960 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4961 {
4962   DM_Plex *mesh = (DM_Plex *)dm->data;
4963   DMLabel  label;
4964   PetscInt pStart, pEnd;
4965 
4966   PetscFunctionBegin;
4967   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4968   if (start) {
4969     PetscValidIntPointer(start, 3);
4970     *start = 0;
4971   }
4972   if (end) {
4973     PetscValidIntPointer(end, 4);
4974     *end = 0;
4975   }
4976   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4977   if (pStart == pEnd) PetscFunctionReturn(0);
4978   if (depth < 0) {
4979     if (start) *start = pStart;
4980     if (end) *end = pEnd;
4981     PetscFunctionReturn(0);
4982   }
4983   if (mesh->tr) {
4984     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
4985   } else {
4986     PetscCall(DMPlexGetDepthLabel(dm, &label));
4987     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4988     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4989   }
4990   PetscFunctionReturn(0);
4991 }
4992 
4993 /*@
4994   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4995 
4996   Not Collective
4997 
4998   Input Parameters:
4999 + dm     - The `DMPLEX` object
5000 - height - The requested height
5001 
5002   Output Parameters:
5003 + start - The first point at this height
5004 - end   - One beyond the last point at this height
5005 
5006   Level: developer
5007 
5008   Notes:
5009   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5010   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5011   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5012 
5013 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5014 @*/
5015 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5016 {
5017   DMLabel  label;
5018   PetscInt depth, pStart, pEnd;
5019 
5020   PetscFunctionBegin;
5021   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5022   if (start) {
5023     PetscValidIntPointer(start, 3);
5024     *start = 0;
5025   }
5026   if (end) {
5027     PetscValidIntPointer(end, 4);
5028     *end = 0;
5029   }
5030   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5031   if (pStart == pEnd) PetscFunctionReturn(0);
5032   if (height < 0) {
5033     if (start) *start = pStart;
5034     if (end) *end = pEnd;
5035     PetscFunctionReturn(0);
5036   }
5037   PetscCall(DMPlexGetDepthLabel(dm, &label));
5038   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5039   PetscCall(DMLabelGetNumValues(label, &depth));
5040   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5041   PetscFunctionReturn(0);
5042 }
5043 
5044 /*@
5045   DMPlexGetPointDepth - Get the depth of a given point
5046 
5047   Not Collective
5048 
5049   Input Parameters:
5050 + dm    - The `DMPLEX` object
5051 - point - The point
5052 
5053   Output Parameter:
5054 . depth - The depth of the point
5055 
5056   Level: intermediate
5057 
5058 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5059 @*/
5060 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5061 {
5062   PetscFunctionBegin;
5063   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5064   PetscValidIntPointer(depth, 3);
5065   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5066   PetscFunctionReturn(0);
5067 }
5068 
5069 /*@
5070   DMPlexGetPointHeight - Get the height of a given point
5071 
5072   Not Collective
5073 
5074   Input Parameters:
5075 + dm    - The `DMPLEX` object
5076 - point - The point
5077 
5078   Output Parameter:
5079 . height - The height of the point
5080 
5081   Level: intermediate
5082 
5083 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5084 @*/
5085 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5086 {
5087   PetscInt n, pDepth;
5088 
5089   PetscFunctionBegin;
5090   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5091   PetscValidIntPointer(height, 3);
5092   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5093   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5094   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5095   PetscFunctionReturn(0);
5096 }
5097 
5098 /*@
5099   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5100 
5101   Not Collective
5102 
5103   Input Parameter:
5104 . dm - The `DMPLEX` object
5105 
5106   Output Parameter:
5107 . celltypeLabel - The `DMLabel` recording cell polytope type
5108 
5109   Level: developer
5110 
5111   Note:
5112   This function will trigger automatica computation of cell types. This can be disabled by calling
5113   `DMCreateLabel`(dm, "celltype") beforehand.
5114 
5115 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5116 @*/
5117 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5118 {
5119   PetscFunctionBegin;
5120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5121   PetscValidPointer(celltypeLabel, 2);
5122   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5123   *celltypeLabel = dm->celltypeLabel;
5124   PetscFunctionReturn(0);
5125 }
5126 
5127 /*@
5128   DMPlexGetCellType - Get the polytope type of a given cell
5129 
5130   Not Collective
5131 
5132   Input Parameters:
5133 + dm   - The `DMPLEX` object
5134 - cell - The cell
5135 
5136   Output Parameter:
5137 . celltype - The polytope type of the cell
5138 
5139   Level: intermediate
5140 
5141 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5142 @*/
5143 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5144 {
5145   DM_Plex *mesh = (DM_Plex *)dm->data;
5146   DMLabel  label;
5147   PetscInt ct;
5148 
5149   PetscFunctionBegin;
5150   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5151   PetscValidPointer(celltype, 3);
5152   if (mesh->tr) {
5153     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5154   } else {
5155     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5156     PetscCall(DMLabelGetValue(label, cell, &ct));
5157     PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5158     *celltype = (DMPolytopeType)ct;
5159   }
5160   PetscFunctionReturn(0);
5161 }
5162 
5163 /*@
5164   DMPlexSetCellType - Set the polytope type of a given cell
5165 
5166   Not Collective
5167 
5168   Input Parameters:
5169 + dm   - The `DMPLEX` object
5170 . cell - The cell
5171 - celltype - The polytope type of the cell
5172 
5173   Level: advanced
5174 
5175   Note:
5176   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5177   is executed. This function will override the computed type. However, if automatic classification will not succeed
5178   and a user wants to manually specify all types, the classification must be disabled by calling
5179   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5180 
5181 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5182 @*/
5183 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5184 {
5185   DMLabel label;
5186 
5187   PetscFunctionBegin;
5188   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5189   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5190   PetscCall(DMLabelSetValue(label, cell, celltype));
5191   PetscFunctionReturn(0);
5192 }
5193 
5194 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5195 {
5196   PetscSection section, s;
5197   Mat          m;
5198   PetscInt     maxHeight;
5199   const char  *prefix;
5200 
5201   PetscFunctionBegin;
5202   PetscCall(DMClone(dm, cdm));
5203   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5204   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5205   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5206   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5207   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5208   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5209   PetscCall(DMSetLocalSection(*cdm, section));
5210   PetscCall(PetscSectionDestroy(&section));
5211   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5212   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5213   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5214   PetscCall(PetscSectionDestroy(&s));
5215   PetscCall(MatDestroy(&m));
5216 
5217   PetscCall(DMSetNumFields(*cdm, 1));
5218   PetscCall(DMCreateDS(*cdm));
5219   (*cdm)->cloneOpts = PETSC_TRUE;
5220   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5221   PetscFunctionReturn(0);
5222 }
5223 
5224 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5225 {
5226   Vec coordsLocal, cellCoordsLocal;
5227   DM  coordsDM, cellCoordsDM;
5228 
5229   PetscFunctionBegin;
5230   *field = NULL;
5231   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5232   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5233   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5234   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5235   if (coordsLocal && coordsDM) {
5236     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5237     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5238   }
5239   PetscFunctionReturn(0);
5240 }
5241 
5242 /*@C
5243   DMPlexGetConeSection - Return a section which describes the layout of cone data
5244 
5245   Not Collective
5246 
5247   Input Parameters:
5248 . dm        - The `DMPLEX` object
5249 
5250   Output Parameter:
5251 . section - The `PetscSection` object
5252 
5253   Level: developer
5254 
5255 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5256 @*/
5257 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5258 {
5259   DM_Plex *mesh = (DM_Plex *)dm->data;
5260 
5261   PetscFunctionBegin;
5262   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5263   if (section) *section = mesh->coneSection;
5264   PetscFunctionReturn(0);
5265 }
5266 
5267 /*@C
5268   DMPlexGetSupportSection - Return a section which describes the layout of support data
5269 
5270   Not Collective
5271 
5272   Input Parameters:
5273 . dm        - The `DMPLEX` object
5274 
5275   Output Parameter:
5276 . section - The `PetscSection` object
5277 
5278   Level: developer
5279 
5280 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5281 @*/
5282 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5283 {
5284   DM_Plex *mesh = (DM_Plex *)dm->data;
5285 
5286   PetscFunctionBegin;
5287   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5288   if (section) *section = mesh->supportSection;
5289   PetscFunctionReturn(0);
5290 }
5291 
5292 /*@C
5293   DMPlexGetCones - Return cone data
5294 
5295   Not Collective
5296 
5297   Input Parameters:
5298 . dm        - The `DMPLEX` object
5299 
5300   Output Parameter:
5301 . cones - The cone for each point
5302 
5303   Level: developer
5304 
5305 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5306 @*/
5307 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5308 {
5309   DM_Plex *mesh = (DM_Plex *)dm->data;
5310 
5311   PetscFunctionBegin;
5312   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5313   if (cones) *cones = mesh->cones;
5314   PetscFunctionReturn(0);
5315 }
5316 
5317 /*@C
5318   DMPlexGetConeOrientations - Return cone orientation data
5319 
5320   Not Collective
5321 
5322   Input Parameters:
5323 . dm        - The `DMPLEX` object
5324 
5325   Output Parameter:
5326 . coneOrientations - The array of cone orientations for all points
5327 
5328   Level: developer
5329 
5330   Notes:
5331   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5332 
5333   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5334 
5335 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5336 @*/
5337 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5338 {
5339   DM_Plex *mesh = (DM_Plex *)dm->data;
5340 
5341   PetscFunctionBegin;
5342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5343   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5344   PetscFunctionReturn(0);
5345 }
5346 
5347 /******************************** FEM Support **********************************/
5348 
5349 /*
5350  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5351  representing a line in the section.
5352 */
5353 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5354 {
5355   PetscFunctionBeginHot;
5356   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5357   if (line < 0) {
5358     *k  = 0;
5359     *Nc = 0;
5360   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5361     *k = 1;
5362   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5363     /* An order k SEM disc has k-1 dofs on an edge */
5364     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5365     *k = *k / *Nc + 1;
5366   }
5367   PetscFunctionReturn(0);
5368 }
5369 
5370 /*@
5371 
5372   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5373   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5374   section provided (or the section of the DM).
5375 
5376   Input Parameters:
5377 + dm      - The DM
5378 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5379 - section - The PetscSection to reorder, or NULL for the default section
5380 
5381   Example:
5382   A typical interpolated single-quad mesh might order points as
5383 .vb
5384   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5385 
5386   v4 -- e6 -- v3
5387   |           |
5388   e7    c0    e8
5389   |           |
5390   v1 -- e5 -- v2
5391 .ve
5392 
5393   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5394   dofs in the order of points, e.g.,
5395 .vb
5396     c0 -> [0,1,2,3]
5397     v1 -> [4]
5398     ...
5399     e5 -> [8, 9]
5400 .ve
5401 
5402   which corresponds to the dofs
5403 .vb
5404     6   10  11  7
5405     13  2   3   15
5406     12  0   1   14
5407     4   8   9   5
5408 .ve
5409 
5410   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5411 .vb
5412   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5413 .ve
5414 
5415   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5416 .vb
5417    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5418 .ve
5419 
5420   Level: developer
5421 
5422   Note:
5423   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5424   degree of the basis.
5425 
5426 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5427 @*/
5428 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5429 {
5430   DMLabel   label;
5431   PetscInt  dim, depth = -1, eStart = -1, Nf;
5432   PetscBool vertexchart;
5433 
5434   PetscFunctionBegin;
5435   PetscCall(DMGetDimension(dm, &dim));
5436   if (dim < 1) PetscFunctionReturn(0);
5437   if (point < 0) {
5438     PetscInt sStart, sEnd;
5439 
5440     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5441     point = sEnd - sStart ? sStart : point;
5442   }
5443   PetscCall(DMPlexGetDepthLabel(dm, &label));
5444   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5445   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5446   if (depth == 1) {
5447     eStart = point;
5448   } else if (depth == dim) {
5449     const PetscInt *cone;
5450 
5451     PetscCall(DMPlexGetCone(dm, point, &cone));
5452     if (dim == 2) eStart = cone[0];
5453     else if (dim == 3) {
5454       const PetscInt *cone2;
5455       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5456       eStart = cone2[0];
5457     } 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);
5458   } 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);
5459   { /* Determine whether the chart covers all points or just vertices. */
5460     PetscInt pStart, pEnd, cStart, cEnd;
5461     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5462     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5463     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5464     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5465     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5466   }
5467   PetscCall(PetscSectionGetNumFields(section, &Nf));
5468   for (PetscInt d = 1; d <= dim; d++) {
5469     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5470     PetscInt *perm;
5471 
5472     for (f = 0; f < Nf; ++f) {
5473       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5474       size += PetscPowInt(k + 1, d) * Nc;
5475     }
5476     PetscCall(PetscMalloc1(size, &perm));
5477     for (f = 0; f < Nf; ++f) {
5478       switch (d) {
5479       case 1:
5480         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5481         /*
5482          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5483          We want              [ vtx0; edge of length k-1; vtx1 ]
5484          */
5485         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5486         for (i = 0; i < k - 1; i++)
5487           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5488         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5489         foffset = offset;
5490         break;
5491       case 2:
5492         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5493         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5494         /* The SEM order is
5495 
5496          v_lb, {e_b}, v_rb,
5497          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5498          v_lt, reverse {e_t}, v_rt
5499          */
5500         {
5501           const PetscInt of   = 0;
5502           const PetscInt oeb  = of + PetscSqr(k - 1);
5503           const PetscInt oer  = oeb + (k - 1);
5504           const PetscInt oet  = oer + (k - 1);
5505           const PetscInt oel  = oet + (k - 1);
5506           const PetscInt ovlb = oel + (k - 1);
5507           const PetscInt ovrb = ovlb + 1;
5508           const PetscInt ovrt = ovrb + 1;
5509           const PetscInt ovlt = ovrt + 1;
5510           PetscInt       o;
5511 
5512           /* bottom */
5513           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5514           for (o = oeb; o < oer; ++o)
5515             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5516           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5517           /* middle */
5518           for (i = 0; i < k - 1; ++i) {
5519             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5520             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5521               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5522             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5523           }
5524           /* top */
5525           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5526           for (o = oel - 1; o >= oet; --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] = ovrt * Nc + c + foffset;
5529           foffset = offset;
5530         }
5531         break;
5532       case 3:
5533         /* The original hex closure is
5534 
5535          {c,
5536          f_b, f_t, f_f, f_b, f_r, f_l,
5537          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5538          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5539          */
5540         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5541         /* The SEM order is
5542          Bottom Slice
5543          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5544          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5545          v_blb, {e_bb}, v_brb,
5546 
5547          Middle Slice (j)
5548          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5549          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5550          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5551 
5552          Top Slice
5553          v_tlf, {e_tf}, v_trf,
5554          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5555          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5556          */
5557         {
5558           const PetscInt oc    = 0;
5559           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5560           const PetscInt oft   = ofb + PetscSqr(k - 1);
5561           const PetscInt off   = oft + PetscSqr(k - 1);
5562           const PetscInt ofk   = off + PetscSqr(k - 1);
5563           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5564           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5565           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5566           const PetscInt oebb  = oebl + (k - 1);
5567           const PetscInt oebr  = oebb + (k - 1);
5568           const PetscInt oebf  = oebr + (k - 1);
5569           const PetscInt oetf  = oebf + (k - 1);
5570           const PetscInt oetr  = oetf + (k - 1);
5571           const PetscInt oetb  = oetr + (k - 1);
5572           const PetscInt oetl  = oetb + (k - 1);
5573           const PetscInt oerf  = oetl + (k - 1);
5574           const PetscInt oelf  = oerf + (k - 1);
5575           const PetscInt oelb  = oelf + (k - 1);
5576           const PetscInt oerb  = oelb + (k - 1);
5577           const PetscInt ovblf = oerb + (k - 1);
5578           const PetscInt ovblb = ovblf + 1;
5579           const PetscInt ovbrb = ovblb + 1;
5580           const PetscInt ovbrf = ovbrb + 1;
5581           const PetscInt ovtlf = ovbrf + 1;
5582           const PetscInt ovtrf = ovtlf + 1;
5583           const PetscInt ovtrb = ovtrf + 1;
5584           const PetscInt ovtlb = ovtrb + 1;
5585           PetscInt       o, n;
5586 
5587           /* Bottom Slice */
5588           /*   bottom */
5589           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5590           for (o = oetf - 1; o >= oebf; --o)
5591             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5592           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5593           /*   middle */
5594           for (i = 0; i < k - 1; ++i) {
5595             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5596             for (n = 0; n < k - 1; ++n) {
5597               o = ofb + n * (k - 1) + i;
5598               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5599             }
5600             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5601           }
5602           /*   top */
5603           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5604           for (o = oebb; o < oebr; ++o)
5605             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5606           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5607 
5608           /* Middle Slice */
5609           for (j = 0; j < k - 1; ++j) {
5610             /*   bottom */
5611             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5612             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5613               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5614             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5615             /*   middle */
5616             for (i = 0; i < k - 1; ++i) {
5617               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5618               for (n = 0; n < k - 1; ++n)
5619                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5620               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5621             }
5622             /*   top */
5623             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5624             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (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] = (oerb + (k - 2) - j) * Nc + c + foffset;
5627           }
5628 
5629           /* Top Slice */
5630           /*   bottom */
5631           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5632           for (o = oetf; o < oetr; ++o)
5633             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5634           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5635           /*   middle */
5636           for (i = 0; i < k - 1; ++i) {
5637             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5638             for (n = 0; n < k - 1; ++n)
5639               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5640             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5641           }
5642           /*   top */
5643           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5644           for (o = oetl - 1; o >= oetb; --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] = ovtrb * Nc + c + foffset;
5647 
5648           foffset = offset;
5649         }
5650         break;
5651       default:
5652         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5653       }
5654     }
5655     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5656     /* Check permutation */
5657     {
5658       PetscInt *check;
5659 
5660       PetscCall(PetscMalloc1(size, &check));
5661       for (i = 0; i < size; ++i) {
5662         check[i] = -1;
5663         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5664       }
5665       for (i = 0; i < size; ++i) check[perm[i]] = i;
5666       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5667       PetscCall(PetscFree(check));
5668     }
5669     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5670     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5671       PetscInt *loc_perm;
5672       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5673       for (PetscInt i = 0; i < size; i++) {
5674         loc_perm[i]        = perm[i];
5675         loc_perm[size + i] = size + perm[i];
5676       }
5677       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5678     }
5679   }
5680   PetscFunctionReturn(0);
5681 }
5682 
5683 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5684 {
5685   PetscDS  prob;
5686   PetscInt depth, Nf, h;
5687   DMLabel  label;
5688 
5689   PetscFunctionBeginHot;
5690   PetscCall(DMGetDS(dm, &prob));
5691   Nf      = prob->Nf;
5692   label   = dm->depthLabel;
5693   *dspace = NULL;
5694   if (field < Nf) {
5695     PetscObject disc = prob->disc[field];
5696 
5697     if (disc->classid == PETSCFE_CLASSID) {
5698       PetscDualSpace dsp;
5699 
5700       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5701       PetscCall(DMLabelGetNumValues(label, &depth));
5702       PetscCall(DMLabelGetValue(label, point, &h));
5703       h = depth - 1 - h;
5704       if (h) {
5705         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5706       } else {
5707         *dspace = dsp;
5708       }
5709     }
5710   }
5711   PetscFunctionReturn(0);
5712 }
5713 
5714 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5715 {
5716   PetscScalar       *array;
5717   const PetscScalar *vArray;
5718   const PetscInt    *cone, *coneO;
5719   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5720 
5721   PetscFunctionBeginHot;
5722   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5723   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5724   PetscCall(DMPlexGetCone(dm, point, &cone));
5725   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5726   if (!values || !*values) {
5727     if ((point >= pStart) && (point < pEnd)) {
5728       PetscInt dof;
5729 
5730       PetscCall(PetscSectionGetDof(section, point, &dof));
5731       size += dof;
5732     }
5733     for (p = 0; p < numPoints; ++p) {
5734       const PetscInt cp = cone[p];
5735       PetscInt       dof;
5736 
5737       if ((cp < pStart) || (cp >= pEnd)) continue;
5738       PetscCall(PetscSectionGetDof(section, cp, &dof));
5739       size += dof;
5740     }
5741     if (!values) {
5742       if (csize) *csize = size;
5743       PetscFunctionReturn(0);
5744     }
5745     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5746   } else {
5747     array = *values;
5748   }
5749   size = 0;
5750   PetscCall(VecGetArrayRead(v, &vArray));
5751   if ((point >= pStart) && (point < pEnd)) {
5752     PetscInt           dof, off, d;
5753     const PetscScalar *varr;
5754 
5755     PetscCall(PetscSectionGetDof(section, point, &dof));
5756     PetscCall(PetscSectionGetOffset(section, point, &off));
5757     varr = &vArray[off];
5758     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5759     size += dof;
5760   }
5761   for (p = 0; p < numPoints; ++p) {
5762     const PetscInt     cp = cone[p];
5763     PetscInt           o  = coneO[p];
5764     PetscInt           dof, off, d;
5765     const PetscScalar *varr;
5766 
5767     if ((cp < pStart) || (cp >= pEnd)) continue;
5768     PetscCall(PetscSectionGetDof(section, cp, &dof));
5769     PetscCall(PetscSectionGetOffset(section, cp, &off));
5770     varr = &vArray[off];
5771     if (o >= 0) {
5772       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5773     } else {
5774       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5775     }
5776     size += dof;
5777   }
5778   PetscCall(VecRestoreArrayRead(v, &vArray));
5779   if (!*values) {
5780     if (csize) *csize = size;
5781     *values = array;
5782   } else {
5783     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5784     *csize = size;
5785   }
5786   PetscFunctionReturn(0);
5787 }
5788 
5789 /* Compress out points not in the section */
5790 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5791 {
5792   const PetscInt np = *numPoints;
5793   PetscInt       pStart, pEnd, p, q;
5794 
5795   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5796   for (p = 0, q = 0; p < np; ++p) {
5797     const PetscInt r = points[p * 2];
5798     if ((r >= pStart) && (r < pEnd)) {
5799       points[q * 2]     = r;
5800       points[q * 2 + 1] = points[p * 2 + 1];
5801       ++q;
5802     }
5803   }
5804   *numPoints = q;
5805   return 0;
5806 }
5807 
5808 /* Compressed closure does not apply closure permutation */
5809 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5810 {
5811   const PetscInt *cla = NULL;
5812   PetscInt        np, *pts = NULL;
5813 
5814   PetscFunctionBeginHot;
5815   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5816   if (*clPoints) {
5817     PetscInt dof, off;
5818 
5819     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5820     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5821     PetscCall(ISGetIndices(*clPoints, &cla));
5822     np  = dof / 2;
5823     pts = (PetscInt *)&cla[off];
5824   } else {
5825     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5826     PetscCall(CompressPoints_Private(section, &np, pts));
5827   }
5828   *numPoints = np;
5829   *points    = pts;
5830   *clp       = cla;
5831   PetscFunctionReturn(0);
5832 }
5833 
5834 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5835 {
5836   PetscFunctionBeginHot;
5837   if (!*clPoints) {
5838     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5839   } else {
5840     PetscCall(ISRestoreIndices(*clPoints, clp));
5841   }
5842   *numPoints = 0;
5843   *points    = NULL;
5844   *clSec     = NULL;
5845   *clPoints  = NULL;
5846   *clp       = NULL;
5847   PetscFunctionReturn(0);
5848 }
5849 
5850 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5851 {
5852   PetscInt            offset = 0, p;
5853   const PetscInt    **perms  = NULL;
5854   const PetscScalar **flips  = NULL;
5855 
5856   PetscFunctionBeginHot;
5857   *size = 0;
5858   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5859   for (p = 0; p < numPoints; p++) {
5860     const PetscInt     point = points[2 * p];
5861     const PetscInt    *perm  = perms ? perms[p] : NULL;
5862     const PetscScalar *flip  = flips ? flips[p] : NULL;
5863     PetscInt           dof, off, d;
5864     const PetscScalar *varr;
5865 
5866     PetscCall(PetscSectionGetDof(section, point, &dof));
5867     PetscCall(PetscSectionGetOffset(section, point, &off));
5868     varr = &vArray[off];
5869     if (clperm) {
5870       if (perm) {
5871         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5872       } else {
5873         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5874       }
5875       if (flip) {
5876         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5877       }
5878     } else {
5879       if (perm) {
5880         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5881       } else {
5882         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5883       }
5884       if (flip) {
5885         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5886       }
5887     }
5888     offset += dof;
5889   }
5890   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5891   *size = offset;
5892   PetscFunctionReturn(0);
5893 }
5894 
5895 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[])
5896 {
5897   PetscInt offset = 0, f;
5898 
5899   PetscFunctionBeginHot;
5900   *size = 0;
5901   for (f = 0; f < numFields; ++f) {
5902     PetscInt            p;
5903     const PetscInt    **perms = NULL;
5904     const PetscScalar **flips = NULL;
5905 
5906     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5907     for (p = 0; p < numPoints; p++) {
5908       const PetscInt     point = points[2 * p];
5909       PetscInt           fdof, foff, b;
5910       const PetscScalar *varr;
5911       const PetscInt    *perm = perms ? perms[p] : NULL;
5912       const PetscScalar *flip = flips ? flips[p] : NULL;
5913 
5914       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5915       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5916       varr = &vArray[foff];
5917       if (clperm) {
5918         if (perm) {
5919           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5920         } else {
5921           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5922         }
5923         if (flip) {
5924           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5925         }
5926       } else {
5927         if (perm) {
5928           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5929         } else {
5930           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5931         }
5932         if (flip) {
5933           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5934         }
5935       }
5936       offset += fdof;
5937     }
5938     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5939   }
5940   *size = offset;
5941   PetscFunctionReturn(0);
5942 }
5943 
5944 /*@C
5945   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5946 
5947   Not collective
5948 
5949   Input Parameters:
5950 + dm - The `DM`
5951 . section - The section describing the layout in v, or NULL to use the default section
5952 . v - The local vector
5953 - point - The point in the `DM`
5954 
5955   Input/Output Parameters:
5956 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5957 - values - An array to use for the values, or NULL to have it allocated automatically;
5958            if the user provided NULL, it is a borrowed array and should not be freed
5959 
5960   Level: intermediate
5961 
5962   Notes:
5963   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to NULL in the
5964   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
5965   assembly function, and a user may already have allocated storage for this operation.
5966 
5967   A typical use could be
5968 .vb
5969    values = NULL;
5970    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5971    for (cl = 0; cl < clSize; ++cl) {
5972      <Compute on closure>
5973    }
5974    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5975 .ve
5976   or
5977 .vb
5978    PetscMalloc1(clMaxSize, &values);
5979    for (p = pStart; p < pEnd; ++p) {
5980      clSize = clMaxSize;
5981      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5982      for (cl = 0; cl < clSize; ++cl) {
5983        <Compute on closure>
5984      }
5985    }
5986    PetscFree(values);
5987 .ve
5988 
5989   Fortran Note:
5990   The csize argument is not present in the Fortran binding since it is internal to the array.
5991 
5992 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5993 @*/
5994 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5995 {
5996   PetscSection    clSection;
5997   IS              clPoints;
5998   PetscInt       *points = NULL;
5999   const PetscInt *clp, *perm;
6000   PetscInt        depth, numFields, numPoints, asize;
6001 
6002   PetscFunctionBeginHot;
6003   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6004   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6005   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6006   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6007   PetscCall(DMPlexGetDepth(dm, &depth));
6008   PetscCall(PetscSectionGetNumFields(section, &numFields));
6009   if (depth == 1 && numFields < 2) {
6010     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6011     PetscFunctionReturn(0);
6012   }
6013   /* Get points */
6014   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6015   /* Get sizes */
6016   asize = 0;
6017   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6018     PetscInt dof;
6019     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6020     asize += dof;
6021   }
6022   if (values) {
6023     const PetscScalar *vArray;
6024     PetscInt           size;
6025 
6026     if (*values) {
6027       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);
6028     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6029     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6030     PetscCall(VecGetArrayRead(v, &vArray));
6031     /* Get values */
6032     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6033     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6034     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6035     /* Cleanup array */
6036     PetscCall(VecRestoreArrayRead(v, &vArray));
6037   }
6038   if (csize) *csize = asize;
6039   /* Cleanup points */
6040   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6041   PetscFunctionReturn(0);
6042 }
6043 
6044 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6045 {
6046   DMLabel            depthLabel;
6047   PetscSection       clSection;
6048   IS                 clPoints;
6049   PetscScalar       *array;
6050   const PetscScalar *vArray;
6051   PetscInt          *points = NULL;
6052   const PetscInt    *clp, *perm = NULL;
6053   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6054 
6055   PetscFunctionBeginHot;
6056   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6057   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6058   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6059   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6060   PetscCall(DMPlexGetDepth(dm, &mdepth));
6061   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6062   PetscCall(PetscSectionGetNumFields(section, &numFields));
6063   if (mdepth == 1 && numFields < 2) {
6064     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6065     PetscFunctionReturn(0);
6066   }
6067   /* Get points */
6068   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6069   for (clsize = 0, p = 0; p < Np; p++) {
6070     PetscInt dof;
6071     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6072     clsize += dof;
6073   }
6074   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6075   /* Filter points */
6076   for (p = 0; p < numPoints * 2; p += 2) {
6077     PetscInt dep;
6078 
6079     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6080     if (dep != depth) continue;
6081     points[Np * 2 + 0] = points[p];
6082     points[Np * 2 + 1] = points[p + 1];
6083     ++Np;
6084   }
6085   /* Get array */
6086   if (!values || !*values) {
6087     PetscInt asize = 0, dof;
6088 
6089     for (p = 0; p < Np * 2; p += 2) {
6090       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6091       asize += dof;
6092     }
6093     if (!values) {
6094       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6095       if (csize) *csize = asize;
6096       PetscFunctionReturn(0);
6097     }
6098     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6099   } else {
6100     array = *values;
6101   }
6102   PetscCall(VecGetArrayRead(v, &vArray));
6103   /* Get values */
6104   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6105   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6106   /* Cleanup points */
6107   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6108   /* Cleanup array */
6109   PetscCall(VecRestoreArrayRead(v, &vArray));
6110   if (!*values) {
6111     if (csize) *csize = size;
6112     *values = array;
6113   } else {
6114     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6115     *csize = size;
6116   }
6117   PetscFunctionReturn(0);
6118 }
6119 
6120 /*@C
6121   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6122 
6123   Not collective
6124 
6125   Input Parameters:
6126 + dm - The `DM`
6127 . section - The section describing the layout in v, or NULL to use the default section
6128 . v - The local vector
6129 . point - The point in the `DM`
6130 . csize - The number of values in the closure, or NULL
6131 - values - The array of values, which is a borrowed array and should not be freed
6132 
6133   Level: intermediate
6134 
6135   Note:
6136   The array values are discarded and not copied back into v. In order to copy values back to v, use `DMPlexVecSetClosure()`
6137 
6138   Fortran Note:
6139   The csize argument is not present in the Fortran binding since it is internal to the array.
6140 
6141 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6142 @*/
6143 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6144 {
6145   PetscInt size = 0;
6146 
6147   PetscFunctionBegin;
6148   /* Should work without recalculating size */
6149   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6150   *values = NULL;
6151   PetscFunctionReturn(0);
6152 }
6153 
6154 static inline void add(PetscScalar *x, PetscScalar y)
6155 {
6156   *x += y;
6157 }
6158 static inline void insert(PetscScalar *x, PetscScalar y)
6159 {
6160   *x = y;
6161 }
6162 
6163 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[])
6164 {
6165   PetscInt        cdof;  /* The number of constraints on this point */
6166   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6167   PetscScalar    *a;
6168   PetscInt        off, cind = 0, k;
6169 
6170   PetscFunctionBegin;
6171   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6172   PetscCall(PetscSectionGetOffset(section, point, &off));
6173   a = &array[off];
6174   if (!cdof || setBC) {
6175     if (clperm) {
6176       if (perm) {
6177         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6178       } else {
6179         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6180       }
6181     } else {
6182       if (perm) {
6183         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6184       } else {
6185         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6186       }
6187     }
6188   } else {
6189     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6190     if (clperm) {
6191       if (perm) {
6192         for (k = 0; k < dof; ++k) {
6193           if ((cind < cdof) && (k == cdofs[cind])) {
6194             ++cind;
6195             continue;
6196           }
6197           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6198         }
6199       } else {
6200         for (k = 0; k < dof; ++k) {
6201           if ((cind < cdof) && (k == cdofs[cind])) {
6202             ++cind;
6203             continue;
6204           }
6205           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6206         }
6207       }
6208     } else {
6209       if (perm) {
6210         for (k = 0; k < dof; ++k) {
6211           if ((cind < cdof) && (k == cdofs[cind])) {
6212             ++cind;
6213             continue;
6214           }
6215           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6216         }
6217       } else {
6218         for (k = 0; k < dof; ++k) {
6219           if ((cind < cdof) && (k == cdofs[cind])) {
6220             ++cind;
6221             continue;
6222           }
6223           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6224         }
6225       }
6226     }
6227   }
6228   PetscFunctionReturn(0);
6229 }
6230 
6231 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[])
6232 {
6233   PetscInt        cdof;  /* The number of constraints on this point */
6234   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6235   PetscScalar    *a;
6236   PetscInt        off, cind = 0, k;
6237 
6238   PetscFunctionBegin;
6239   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6240   PetscCall(PetscSectionGetOffset(section, point, &off));
6241   a = &array[off];
6242   if (cdof) {
6243     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6244     if (clperm) {
6245       if (perm) {
6246         for (k = 0; k < dof; ++k) {
6247           if ((cind < cdof) && (k == cdofs[cind])) {
6248             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6249             cind++;
6250           }
6251         }
6252       } else {
6253         for (k = 0; k < dof; ++k) {
6254           if ((cind < cdof) && (k == cdofs[cind])) {
6255             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6256             cind++;
6257           }
6258         }
6259       }
6260     } else {
6261       if (perm) {
6262         for (k = 0; k < dof; ++k) {
6263           if ((cind < cdof) && (k == cdofs[cind])) {
6264             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6265             cind++;
6266           }
6267         }
6268       } else {
6269         for (k = 0; k < dof; ++k) {
6270           if ((cind < cdof) && (k == cdofs[cind])) {
6271             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6272             cind++;
6273           }
6274         }
6275       }
6276     }
6277   }
6278   PetscFunctionReturn(0);
6279 }
6280 
6281 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[])
6282 {
6283   PetscScalar    *a;
6284   PetscInt        fdof, foff, fcdof, foffset = *offset;
6285   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6286   PetscInt        cind = 0, b;
6287 
6288   PetscFunctionBegin;
6289   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6290   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6291   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6292   a = &array[foff];
6293   if (!fcdof || setBC) {
6294     if (clperm) {
6295       if (perm) {
6296         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6297       } else {
6298         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6299       }
6300     } else {
6301       if (perm) {
6302         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6303       } else {
6304         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6305       }
6306     }
6307   } else {
6308     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6309     if (clperm) {
6310       if (perm) {
6311         for (b = 0; b < fdof; b++) {
6312           if ((cind < fcdof) && (b == fcdofs[cind])) {
6313             ++cind;
6314             continue;
6315           }
6316           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6317         }
6318       } else {
6319         for (b = 0; b < fdof; b++) {
6320           if ((cind < fcdof) && (b == fcdofs[cind])) {
6321             ++cind;
6322             continue;
6323           }
6324           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6325         }
6326       }
6327     } else {
6328       if (perm) {
6329         for (b = 0; b < fdof; b++) {
6330           if ((cind < fcdof) && (b == fcdofs[cind])) {
6331             ++cind;
6332             continue;
6333           }
6334           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6335         }
6336       } else {
6337         for (b = 0; b < fdof; b++) {
6338           if ((cind < fcdof) && (b == fcdofs[cind])) {
6339             ++cind;
6340             continue;
6341           }
6342           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6343         }
6344       }
6345     }
6346   }
6347   *offset += fdof;
6348   PetscFunctionReturn(0);
6349 }
6350 
6351 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[])
6352 {
6353   PetscScalar    *a;
6354   PetscInt        fdof, foff, fcdof, foffset = *offset;
6355   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6356   PetscInt        Nc, cind = 0, ncind = 0, b;
6357   PetscBool       ncSet, fcSet;
6358 
6359   PetscFunctionBegin;
6360   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6361   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6362   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6363   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6364   a = &array[foff];
6365   if (fcdof) {
6366     /* We just override fcdof and fcdofs with Ncc and comps */
6367     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6368     if (clperm) {
6369       if (perm) {
6370         if (comps) {
6371           for (b = 0; b < fdof; b++) {
6372             ncSet = fcSet = PETSC_FALSE;
6373             if (b % Nc == comps[ncind]) {
6374               ncind = (ncind + 1) % Ncc;
6375               ncSet = PETSC_TRUE;
6376             }
6377             if ((cind < fcdof) && (b == fcdofs[cind])) {
6378               ++cind;
6379               fcSet = PETSC_TRUE;
6380             }
6381             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6382           }
6383         } else {
6384           for (b = 0; b < fdof; b++) {
6385             if ((cind < fcdof) && (b == fcdofs[cind])) {
6386               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6387               ++cind;
6388             }
6389           }
6390         }
6391       } else {
6392         if (comps) {
6393           for (b = 0; b < fdof; b++) {
6394             ncSet = fcSet = PETSC_FALSE;
6395             if (b % Nc == comps[ncind]) {
6396               ncind = (ncind + 1) % Ncc;
6397               ncSet = PETSC_TRUE;
6398             }
6399             if ((cind < fcdof) && (b == fcdofs[cind])) {
6400               ++cind;
6401               fcSet = PETSC_TRUE;
6402             }
6403             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6404           }
6405         } else {
6406           for (b = 0; b < fdof; b++) {
6407             if ((cind < fcdof) && (b == fcdofs[cind])) {
6408               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6409               ++cind;
6410             }
6411           }
6412         }
6413       }
6414     } else {
6415       if (perm) {
6416         if (comps) {
6417           for (b = 0; b < fdof; b++) {
6418             ncSet = fcSet = PETSC_FALSE;
6419             if (b % Nc == comps[ncind]) {
6420               ncind = (ncind + 1) % Ncc;
6421               ncSet = PETSC_TRUE;
6422             }
6423             if ((cind < fcdof) && (b == fcdofs[cind])) {
6424               ++cind;
6425               fcSet = PETSC_TRUE;
6426             }
6427             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6428           }
6429         } else {
6430           for (b = 0; b < fdof; b++) {
6431             if ((cind < fcdof) && (b == fcdofs[cind])) {
6432               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6433               ++cind;
6434             }
6435           }
6436         }
6437       } else {
6438         if (comps) {
6439           for (b = 0; b < fdof; b++) {
6440             ncSet = fcSet = PETSC_FALSE;
6441             if (b % Nc == comps[ncind]) {
6442               ncind = (ncind + 1) % Ncc;
6443               ncSet = PETSC_TRUE;
6444             }
6445             if ((cind < fcdof) && (b == fcdofs[cind])) {
6446               ++cind;
6447               fcSet = PETSC_TRUE;
6448             }
6449             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6450           }
6451         } else {
6452           for (b = 0; b < fdof; b++) {
6453             if ((cind < fcdof) && (b == fcdofs[cind])) {
6454               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6455               ++cind;
6456             }
6457           }
6458         }
6459       }
6460     }
6461   }
6462   *offset += fdof;
6463   PetscFunctionReturn(0);
6464 }
6465 
6466 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6467 {
6468   PetscScalar    *array;
6469   const PetscInt *cone, *coneO;
6470   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6471 
6472   PetscFunctionBeginHot;
6473   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6474   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6475   PetscCall(DMPlexGetCone(dm, point, &cone));
6476   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6477   PetscCall(VecGetArray(v, &array));
6478   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6479     const PetscInt cp = !p ? point : cone[p - 1];
6480     const PetscInt o  = !p ? 0 : coneO[p - 1];
6481 
6482     if ((cp < pStart) || (cp >= pEnd)) {
6483       dof = 0;
6484       continue;
6485     }
6486     PetscCall(PetscSectionGetDof(section, cp, &dof));
6487     /* ADD_VALUES */
6488     {
6489       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6490       PetscScalar    *a;
6491       PetscInt        cdof, coff, cind = 0, k;
6492 
6493       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6494       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6495       a = &array[coff];
6496       if (!cdof) {
6497         if (o >= 0) {
6498           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6499         } else {
6500           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6501         }
6502       } else {
6503         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6504         if (o >= 0) {
6505           for (k = 0; k < dof; ++k) {
6506             if ((cind < cdof) && (k == cdofs[cind])) {
6507               ++cind;
6508               continue;
6509             }
6510             a[k] += values[off + k];
6511           }
6512         } else {
6513           for (k = 0; k < dof; ++k) {
6514             if ((cind < cdof) && (k == cdofs[cind])) {
6515               ++cind;
6516               continue;
6517             }
6518             a[k] += values[off + dof - k - 1];
6519           }
6520         }
6521       }
6522     }
6523   }
6524   PetscCall(VecRestoreArray(v, &array));
6525   PetscFunctionReturn(0);
6526 }
6527 
6528 /*@C
6529   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6530 
6531   Not collective
6532 
6533   Input Parameters:
6534 + dm - The `DM`
6535 . section - The section describing the layout in v, or NULL to use the default section
6536 . v - The local vector
6537 . point - The point in the DM
6538 . values - The array of values
6539 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6540          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6541 
6542   Level: intermediate
6543 
6544 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6545 @*/
6546 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6547 {
6548   PetscSection    clSection;
6549   IS              clPoints;
6550   PetscScalar    *array;
6551   PetscInt       *points = NULL;
6552   const PetscInt *clp, *clperm = NULL;
6553   PetscInt        depth, numFields, numPoints, p, clsize;
6554 
6555   PetscFunctionBeginHot;
6556   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6557   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6558   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6559   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6560   PetscCall(DMPlexGetDepth(dm, &depth));
6561   PetscCall(PetscSectionGetNumFields(section, &numFields));
6562   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6563     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6564     PetscFunctionReturn(0);
6565   }
6566   /* Get points */
6567   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6568   for (clsize = 0, p = 0; p < numPoints; p++) {
6569     PetscInt dof;
6570     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6571     clsize += dof;
6572   }
6573   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6574   /* Get array */
6575   PetscCall(VecGetArray(v, &array));
6576   /* Get values */
6577   if (numFields > 0) {
6578     PetscInt offset = 0, f;
6579     for (f = 0; f < numFields; ++f) {
6580       const PetscInt    **perms = NULL;
6581       const PetscScalar **flips = NULL;
6582 
6583       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6584       switch (mode) {
6585       case INSERT_VALUES:
6586         for (p = 0; p < numPoints; p++) {
6587           const PetscInt     point = points[2 * p];
6588           const PetscInt    *perm  = perms ? perms[p] : NULL;
6589           const PetscScalar *flip  = flips ? flips[p] : NULL;
6590           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6591         }
6592         break;
6593       case INSERT_ALL_VALUES:
6594         for (p = 0; p < numPoints; p++) {
6595           const PetscInt     point = points[2 * p];
6596           const PetscInt    *perm  = perms ? perms[p] : NULL;
6597           const PetscScalar *flip  = flips ? flips[p] : NULL;
6598           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6599         }
6600         break;
6601       case INSERT_BC_VALUES:
6602         for (p = 0; p < numPoints; p++) {
6603           const PetscInt     point = points[2 * p];
6604           const PetscInt    *perm  = perms ? perms[p] : NULL;
6605           const PetscScalar *flip  = flips ? flips[p] : NULL;
6606           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6607         }
6608         break;
6609       case ADD_VALUES:
6610         for (p = 0; p < numPoints; p++) {
6611           const PetscInt     point = points[2 * p];
6612           const PetscInt    *perm  = perms ? perms[p] : NULL;
6613           const PetscScalar *flip  = flips ? flips[p] : NULL;
6614           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6615         }
6616         break;
6617       case ADD_ALL_VALUES:
6618         for (p = 0; p < numPoints; p++) {
6619           const PetscInt     point = points[2 * p];
6620           const PetscInt    *perm  = perms ? perms[p] : NULL;
6621           const PetscScalar *flip  = flips ? flips[p] : NULL;
6622           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6623         }
6624         break;
6625       case ADD_BC_VALUES:
6626         for (p = 0; p < numPoints; p++) {
6627           const PetscInt     point = points[2 * p];
6628           const PetscInt    *perm  = perms ? perms[p] : NULL;
6629           const PetscScalar *flip  = flips ? flips[p] : NULL;
6630           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6631         }
6632         break;
6633       default:
6634         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6635       }
6636       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6637     }
6638   } else {
6639     PetscInt            dof, off;
6640     const PetscInt    **perms = NULL;
6641     const PetscScalar **flips = NULL;
6642 
6643     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6644     switch (mode) {
6645     case INSERT_VALUES:
6646       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6647         const PetscInt     point = points[2 * p];
6648         const PetscInt    *perm  = perms ? perms[p] : NULL;
6649         const PetscScalar *flip  = flips ? flips[p] : NULL;
6650         PetscCall(PetscSectionGetDof(section, point, &dof));
6651         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6652       }
6653       break;
6654     case INSERT_ALL_VALUES:
6655       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6656         const PetscInt     point = points[2 * p];
6657         const PetscInt    *perm  = perms ? perms[p] : NULL;
6658         const PetscScalar *flip  = flips ? flips[p] : NULL;
6659         PetscCall(PetscSectionGetDof(section, point, &dof));
6660         updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array);
6661       }
6662       break;
6663     case INSERT_BC_VALUES:
6664       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6665         const PetscInt     point = points[2 * p];
6666         const PetscInt    *perm  = perms ? perms[p] : NULL;
6667         const PetscScalar *flip  = flips ? flips[p] : NULL;
6668         PetscCall(PetscSectionGetDof(section, point, &dof));
6669         updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array);
6670       }
6671       break;
6672     case ADD_VALUES:
6673       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6674         const PetscInt     point = points[2 * p];
6675         const PetscInt    *perm  = perms ? perms[p] : NULL;
6676         const PetscScalar *flip  = flips ? flips[p] : NULL;
6677         PetscCall(PetscSectionGetDof(section, point, &dof));
6678         updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array);
6679       }
6680       break;
6681     case ADD_ALL_VALUES:
6682       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6683         const PetscInt     point = points[2 * p];
6684         const PetscInt    *perm  = perms ? perms[p] : NULL;
6685         const PetscScalar *flip  = flips ? flips[p] : NULL;
6686         PetscCall(PetscSectionGetDof(section, point, &dof));
6687         updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array);
6688       }
6689       break;
6690     case ADD_BC_VALUES:
6691       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6692         const PetscInt     point = points[2 * p];
6693         const PetscInt    *perm  = perms ? perms[p] : NULL;
6694         const PetscScalar *flip  = flips ? flips[p] : NULL;
6695         PetscCall(PetscSectionGetDof(section, point, &dof));
6696         updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array);
6697       }
6698       break;
6699     default:
6700       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6701     }
6702     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6703   }
6704   /* Cleanup points */
6705   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6706   /* Cleanup array */
6707   PetscCall(VecRestoreArray(v, &array));
6708   PetscFunctionReturn(0);
6709 }
6710 
6711 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6712 {
6713   const PetscInt *supp, *cone;
6714   PetscScalar    *a;
6715   PetscInt        dim, Ns, dof, off, n = 0;
6716 
6717   PetscFunctionBegin;
6718   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6719   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6720   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6721   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6722   if (PetscDefined(USE_DEBUG)) {
6723     PetscInt vStart, vEnd;
6724 
6725     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6726     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);
6727   }
6728   PetscValidScalarPointer(values, 5);
6729 
6730   PetscCall(DMGetDimension(dm, &dim));
6731   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6732   PetscCall(DMPlexGetSupport(dm, point, &supp));
6733   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);
6734   PetscCall(VecGetArray(v, &a));
6735   PetscCall(PetscSectionGetDof(section, point, &dof));
6736   PetscCall(PetscSectionGetOffset(section, point, &off));
6737   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6738   for (PetscInt d = 0; d < dim; ++d) {
6739     // Left edge
6740     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6741     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6742     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6743     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6744     // Right edge
6745     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6746     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6747     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6748     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6749   }
6750   PetscCall(VecRestoreArray(v, &a));
6751   PetscFunctionReturn(0);
6752 }
6753 
6754 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6755 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6756 {
6757   PetscFunctionBegin;
6758   *contains = PETSC_TRUE;
6759   if (label) {
6760     PetscInt fdof;
6761 
6762     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6763     if (!*contains) {
6764       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6765       *offset += fdof;
6766       PetscFunctionReturn(0);
6767     }
6768   }
6769   PetscFunctionReturn(0);
6770 }
6771 
6772 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6773 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)
6774 {
6775   PetscSection    clSection;
6776   IS              clPoints;
6777   PetscScalar    *array;
6778   PetscInt       *points = NULL;
6779   const PetscInt *clp;
6780   PetscInt        numFields, numPoints, p;
6781   PetscInt        offset = 0, f;
6782 
6783   PetscFunctionBeginHot;
6784   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6785   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6786   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6787   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6788   PetscCall(PetscSectionGetNumFields(section, &numFields));
6789   /* Get points */
6790   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6791   /* Get array */
6792   PetscCall(VecGetArray(v, &array));
6793   /* Get values */
6794   for (f = 0; f < numFields; ++f) {
6795     const PetscInt    **perms = NULL;
6796     const PetscScalar **flips = NULL;
6797     PetscBool           contains;
6798 
6799     if (!fieldActive[f]) {
6800       for (p = 0; p < numPoints * 2; p += 2) {
6801         PetscInt fdof;
6802         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6803         offset += fdof;
6804       }
6805       continue;
6806     }
6807     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6808     switch (mode) {
6809     case INSERT_VALUES:
6810       for (p = 0; p < numPoints; p++) {
6811         const PetscInt     point = points[2 * p];
6812         const PetscInt    *perm  = perms ? perms[p] : NULL;
6813         const PetscScalar *flip  = flips ? flips[p] : NULL;
6814         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6815         if (!contains) continue;
6816         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6817       }
6818       break;
6819     case INSERT_ALL_VALUES:
6820       for (p = 0; p < numPoints; p++) {
6821         const PetscInt     point = points[2 * p];
6822         const PetscInt    *perm  = perms ? perms[p] : NULL;
6823         const PetscScalar *flip  = flips ? flips[p] : NULL;
6824         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6825         if (!contains) continue;
6826         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6827       }
6828       break;
6829     case INSERT_BC_VALUES:
6830       for (p = 0; p < numPoints; p++) {
6831         const PetscInt     point = points[2 * p];
6832         const PetscInt    *perm  = perms ? perms[p] : NULL;
6833         const PetscScalar *flip  = flips ? flips[p] : NULL;
6834         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6835         if (!contains) continue;
6836         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6837       }
6838       break;
6839     case ADD_VALUES:
6840       for (p = 0; p < numPoints; p++) {
6841         const PetscInt     point = points[2 * p];
6842         const PetscInt    *perm  = perms ? perms[p] : NULL;
6843         const PetscScalar *flip  = flips ? flips[p] : NULL;
6844         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6845         if (!contains) continue;
6846         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6847       }
6848       break;
6849     case ADD_ALL_VALUES:
6850       for (p = 0; p < numPoints; p++) {
6851         const PetscInt     point = points[2 * p];
6852         const PetscInt    *perm  = perms ? perms[p] : NULL;
6853         const PetscScalar *flip  = flips ? flips[p] : NULL;
6854         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6855         if (!contains) continue;
6856         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6857       }
6858       break;
6859     default:
6860       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6861     }
6862     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6863   }
6864   /* Cleanup points */
6865   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6866   /* Cleanup array */
6867   PetscCall(VecRestoreArray(v, &array));
6868   PetscFunctionReturn(0);
6869 }
6870 
6871 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6872 {
6873   PetscMPIInt rank;
6874   PetscInt    i, j;
6875 
6876   PetscFunctionBegin;
6877   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6878   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6879   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6880   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6881   numCIndices = numCIndices ? numCIndices : numRIndices;
6882   if (!values) PetscFunctionReturn(0);
6883   for (i = 0; i < numRIndices; i++) {
6884     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6885     for (j = 0; j < numCIndices; j++) {
6886 #if defined(PETSC_USE_COMPLEX)
6887       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6888 #else
6889       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6890 #endif
6891     }
6892     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6893   }
6894   PetscFunctionReturn(0);
6895 }
6896 
6897 /*
6898   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6899 
6900   Input Parameters:
6901 + section - The section for this data layout
6902 . islocal - Is the section (and thus indices being requested) local or global?
6903 . point   - The point contributing dofs with these indices
6904 . off     - The global offset of this point
6905 . loff    - The local offset of each field
6906 . setBC   - The flag determining whether to include indices of boundary values
6907 . perm    - A permutation of the dofs on this point, or NULL
6908 - indperm - A permutation of the entire indices array, or NULL
6909 
6910   Output Parameter:
6911 . indices - Indices for dofs on this point
6912 
6913   Level: developer
6914 
6915   Note: The indices could be local or global, depending on the value of 'off'.
6916 */
6917 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6918 {
6919   PetscInt        dof;   /* The number of unknowns on this point */
6920   PetscInt        cdof;  /* The number of constraints on this point */
6921   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6922   PetscInt        cind = 0, k;
6923 
6924   PetscFunctionBegin;
6925   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6926   PetscCall(PetscSectionGetDof(section, point, &dof));
6927   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6928   if (!cdof || setBC) {
6929     for (k = 0; k < dof; ++k) {
6930       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6931       const PetscInt ind    = indperm ? indperm[preind] : preind;
6932 
6933       indices[ind] = off + k;
6934     }
6935   } else {
6936     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6937     for (k = 0; k < dof; ++k) {
6938       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6939       const PetscInt ind    = indperm ? indperm[preind] : preind;
6940 
6941       if ((cind < cdof) && (k == cdofs[cind])) {
6942         /* Insert check for returning constrained indices */
6943         indices[ind] = -(off + k + 1);
6944         ++cind;
6945       } else {
6946         indices[ind] = off + k - (islocal ? 0 : cind);
6947       }
6948     }
6949   }
6950   *loff += dof;
6951   PetscFunctionReturn(0);
6952 }
6953 
6954 /*
6955  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6956 
6957  Input Parameters:
6958 + section - a section (global or local)
6959 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6960 . point - point within section
6961 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6962 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6963 . setBC - identify constrained (boundary condition) points via involution.
6964 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6965 . permsoff - offset
6966 - indperm - index permutation
6967 
6968  Output Parameter:
6969 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6970 . indices - array to hold indices (as defined by section) of each dof associated with point
6971 
6972  Notes:
6973  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6974  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6975  in the local vector.
6976 
6977  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6978  significant).  It is invalid to call with a global section and setBC=true.
6979 
6980  Developer Note:
6981  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6982  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6983  offset could be obtained from the section instead of passing it explicitly as we do now.
6984 
6985  Example:
6986  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6987  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6988  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6989  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.
6990 
6991  Level: developer
6992 */
6993 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[])
6994 {
6995   PetscInt numFields, foff, f;
6996 
6997   PetscFunctionBegin;
6998   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6999   PetscCall(PetscSectionGetNumFields(section, &numFields));
7000   for (f = 0, foff = 0; f < numFields; ++f) {
7001     PetscInt        fdof, cfdof;
7002     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7003     PetscInt        cind = 0, b;
7004     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7005 
7006     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7007     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7008     if (!cfdof || setBC) {
7009       for (b = 0; b < fdof; ++b) {
7010         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7011         const PetscInt ind    = indperm ? indperm[preind] : preind;
7012 
7013         indices[ind] = off + foff + b;
7014       }
7015     } else {
7016       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7017       for (b = 0; b < fdof; ++b) {
7018         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7019         const PetscInt ind    = indperm ? indperm[preind] : preind;
7020 
7021         if ((cind < cfdof) && (b == fcdofs[cind])) {
7022           indices[ind] = -(off + foff + b + 1);
7023           ++cind;
7024         } else {
7025           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7026         }
7027       }
7028     }
7029     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7030     foffs[f] += fdof;
7031   }
7032   PetscFunctionReturn(0);
7033 }
7034 
7035 /*
7036   This version believes the globalSection offsets for each field, rather than just the point offset
7037 
7038  . foffs - The offset into 'indices' for each field, since it is segregated by field
7039 
7040  Notes:
7041  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7042  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7043 */
7044 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7045 {
7046   PetscInt numFields, foff, f;
7047 
7048   PetscFunctionBegin;
7049   PetscCall(PetscSectionGetNumFields(section, &numFields));
7050   for (f = 0; f < numFields; ++f) {
7051     PetscInt        fdof, cfdof;
7052     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7053     PetscInt        cind = 0, b;
7054     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7055 
7056     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7057     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7058     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7059     if (!cfdof) {
7060       for (b = 0; b < fdof; ++b) {
7061         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7062         const PetscInt ind    = indperm ? indperm[preind] : preind;
7063 
7064         indices[ind] = foff + b;
7065       }
7066     } else {
7067       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7068       for (b = 0; b < fdof; ++b) {
7069         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7070         const PetscInt ind    = indperm ? indperm[preind] : preind;
7071 
7072         if ((cind < cfdof) && (b == fcdofs[cind])) {
7073           indices[ind] = -(foff + b + 1);
7074           ++cind;
7075         } else {
7076           indices[ind] = foff + b - cind;
7077         }
7078       }
7079     }
7080     foffs[f] += fdof;
7081   }
7082   PetscFunctionReturn(0);
7083 }
7084 
7085 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)
7086 {
7087   Mat             cMat;
7088   PetscSection    aSec, cSec;
7089   IS              aIS;
7090   PetscInt        aStart = -1, aEnd = -1;
7091   const PetscInt *anchors;
7092   PetscInt        numFields, f, p, q, newP = 0;
7093   PetscInt        newNumPoints = 0, newNumIndices = 0;
7094   PetscInt       *newPoints, *indices, *newIndices;
7095   PetscInt        maxAnchor, maxDof;
7096   PetscInt        newOffsets[32];
7097   PetscInt       *pointMatOffsets[32];
7098   PetscInt       *newPointOffsets[32];
7099   PetscScalar    *pointMat[32];
7100   PetscScalar    *newValues      = NULL, *tmpValues;
7101   PetscBool       anyConstrained = PETSC_FALSE;
7102 
7103   PetscFunctionBegin;
7104   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7105   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7106   PetscCall(PetscSectionGetNumFields(section, &numFields));
7107 
7108   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7109   /* if there are point-to-point constraints */
7110   if (aSec) {
7111     PetscCall(PetscArrayzero(newOffsets, 32));
7112     PetscCall(ISGetIndices(aIS, &anchors));
7113     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7114     /* figure out how many points are going to be in the new element matrix
7115      * (we allow double counting, because it's all just going to be summed
7116      * into the global matrix anyway) */
7117     for (p = 0; p < 2 * numPoints; p += 2) {
7118       PetscInt b    = points[p];
7119       PetscInt bDof = 0, bSecDof;
7120 
7121       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7122       if (!bSecDof) continue;
7123       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7124       if (bDof) {
7125         /* this point is constrained */
7126         /* it is going to be replaced by its anchors */
7127         PetscInt bOff, q;
7128 
7129         anyConstrained = PETSC_TRUE;
7130         newNumPoints += bDof;
7131         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7132         for (q = 0; q < bDof; q++) {
7133           PetscInt a = anchors[bOff + q];
7134           PetscInt aDof;
7135 
7136           PetscCall(PetscSectionGetDof(section, a, &aDof));
7137           newNumIndices += aDof;
7138           for (f = 0; f < numFields; ++f) {
7139             PetscInt fDof;
7140 
7141             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7142             newOffsets[f + 1] += fDof;
7143           }
7144         }
7145       } else {
7146         /* this point is not constrained */
7147         newNumPoints++;
7148         newNumIndices += bSecDof;
7149         for (f = 0; f < numFields; ++f) {
7150           PetscInt fDof;
7151 
7152           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7153           newOffsets[f + 1] += fDof;
7154         }
7155       }
7156     }
7157   }
7158   if (!anyConstrained) {
7159     if (outNumPoints) *outNumPoints = 0;
7160     if (outNumIndices) *outNumIndices = 0;
7161     if (outPoints) *outPoints = NULL;
7162     if (outValues) *outValues = NULL;
7163     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7164     PetscFunctionReturn(0);
7165   }
7166 
7167   if (outNumPoints) *outNumPoints = newNumPoints;
7168   if (outNumIndices) *outNumIndices = newNumIndices;
7169 
7170   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7171 
7172   if (!outPoints && !outValues) {
7173     if (offsets) {
7174       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7175     }
7176     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7177     PetscFunctionReturn(0);
7178   }
7179 
7180   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7181 
7182   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7183 
7184   /* workspaces */
7185   if (numFields) {
7186     for (f = 0; f < numFields; f++) {
7187       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7188       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7189     }
7190   } else {
7191     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7192     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7193   }
7194 
7195   /* get workspaces for the point-to-point matrices */
7196   if (numFields) {
7197     PetscInt totalOffset, totalMatOffset;
7198 
7199     for (p = 0; p < numPoints; p++) {
7200       PetscInt b    = points[2 * p];
7201       PetscInt bDof = 0, bSecDof;
7202 
7203       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7204       if (!bSecDof) {
7205         for (f = 0; f < numFields; f++) {
7206           newPointOffsets[f][p + 1] = 0;
7207           pointMatOffsets[f][p + 1] = 0;
7208         }
7209         continue;
7210       }
7211       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7212       if (bDof) {
7213         for (f = 0; f < numFields; f++) {
7214           PetscInt fDof, q, bOff, allFDof = 0;
7215 
7216           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7217           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7218           for (q = 0; q < bDof; q++) {
7219             PetscInt a = anchors[bOff + q];
7220             PetscInt aFDof;
7221 
7222             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7223             allFDof += aFDof;
7224           }
7225           newPointOffsets[f][p + 1] = allFDof;
7226           pointMatOffsets[f][p + 1] = fDof * allFDof;
7227         }
7228       } else {
7229         for (f = 0; f < numFields; f++) {
7230           PetscInt fDof;
7231 
7232           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7233           newPointOffsets[f][p + 1] = fDof;
7234           pointMatOffsets[f][p + 1] = 0;
7235         }
7236       }
7237     }
7238     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7239       newPointOffsets[f][0] = totalOffset;
7240       pointMatOffsets[f][0] = totalMatOffset;
7241       for (p = 0; p < numPoints; p++) {
7242         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7243         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7244       }
7245       totalOffset    = newPointOffsets[f][numPoints];
7246       totalMatOffset = pointMatOffsets[f][numPoints];
7247       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7248     }
7249   } else {
7250     for (p = 0; p < numPoints; p++) {
7251       PetscInt b    = points[2 * p];
7252       PetscInt bDof = 0, bSecDof;
7253 
7254       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7255       if (!bSecDof) {
7256         newPointOffsets[0][p + 1] = 0;
7257         pointMatOffsets[0][p + 1] = 0;
7258         continue;
7259       }
7260       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7261       if (bDof) {
7262         PetscInt bOff, q, allDof = 0;
7263 
7264         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7265         for (q = 0; q < bDof; q++) {
7266           PetscInt a = anchors[bOff + q], aDof;
7267 
7268           PetscCall(PetscSectionGetDof(section, a, &aDof));
7269           allDof += aDof;
7270         }
7271         newPointOffsets[0][p + 1] = allDof;
7272         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7273       } else {
7274         newPointOffsets[0][p + 1] = bSecDof;
7275         pointMatOffsets[0][p + 1] = 0;
7276       }
7277     }
7278     newPointOffsets[0][0] = 0;
7279     pointMatOffsets[0][0] = 0;
7280     for (p = 0; p < numPoints; p++) {
7281       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7282       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7283     }
7284     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7285   }
7286 
7287   /* output arrays */
7288   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7289 
7290   /* get the point-to-point matrices; construct newPoints */
7291   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7292   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7293   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7294   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7295   if (numFields) {
7296     for (p = 0, newP = 0; p < numPoints; p++) {
7297       PetscInt b    = points[2 * p];
7298       PetscInt o    = points[2 * p + 1];
7299       PetscInt bDof = 0, bSecDof;
7300 
7301       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7302       if (!bSecDof) continue;
7303       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7304       if (bDof) {
7305         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7306 
7307         fStart[0] = 0;
7308         fEnd[0]   = 0;
7309         for (f = 0; f < numFields; f++) {
7310           PetscInt fDof;
7311 
7312           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7313           fStart[f + 1] = fStart[f] + fDof;
7314           fEnd[f + 1]   = fStart[f + 1];
7315         }
7316         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7317         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7318 
7319         fAnchorStart[0] = 0;
7320         fAnchorEnd[0]   = 0;
7321         for (f = 0; f < numFields; f++) {
7322           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7323 
7324           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7325           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7326         }
7327         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7328         for (q = 0; q < bDof; q++) {
7329           PetscInt a = anchors[bOff + q], aOff;
7330 
7331           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7332           newPoints[2 * (newP + q)]     = a;
7333           newPoints[2 * (newP + q) + 1] = 0;
7334           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7335           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7336         }
7337         newP += bDof;
7338 
7339         if (outValues) {
7340           /* get the point-to-point submatrix */
7341           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]));
7342         }
7343       } else {
7344         newPoints[2 * newP]     = b;
7345         newPoints[2 * newP + 1] = o;
7346         newP++;
7347       }
7348     }
7349   } else {
7350     for (p = 0; p < numPoints; p++) {
7351       PetscInt b    = points[2 * p];
7352       PetscInt o    = points[2 * p + 1];
7353       PetscInt bDof = 0, bSecDof;
7354 
7355       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7356       if (!bSecDof) continue;
7357       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7358       if (bDof) {
7359         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7360 
7361         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7362         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7363 
7364         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7365         for (q = 0; q < bDof; q++) {
7366           PetscInt a = anchors[bOff + q], aOff;
7367 
7368           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7369 
7370           newPoints[2 * (newP + q)]     = a;
7371           newPoints[2 * (newP + q) + 1] = 0;
7372           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7373           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7374         }
7375         newP += bDof;
7376 
7377         /* get the point-to-point submatrix */
7378         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7379       } else {
7380         newPoints[2 * newP]     = b;
7381         newPoints[2 * newP + 1] = o;
7382         newP++;
7383       }
7384     }
7385   }
7386 
7387   if (outValues) {
7388     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7389     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7390     /* multiply constraints on the right */
7391     if (numFields) {
7392       for (f = 0; f < numFields; f++) {
7393         PetscInt oldOff = offsets[f];
7394 
7395         for (p = 0; p < numPoints; p++) {
7396           PetscInt cStart = newPointOffsets[f][p];
7397           PetscInt b      = points[2 * p];
7398           PetscInt c, r, k;
7399           PetscInt dof;
7400 
7401           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7402           if (!dof) continue;
7403           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7404             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7405             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7406 
7407             for (r = 0; r < numIndices; r++) {
7408               for (c = 0; c < nCols; c++) {
7409                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7410               }
7411             }
7412           } else {
7413             /* copy this column as is */
7414             for (r = 0; r < numIndices; r++) {
7415               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7416             }
7417           }
7418           oldOff += dof;
7419         }
7420       }
7421     } else {
7422       PetscInt oldOff = 0;
7423       for (p = 0; p < numPoints; p++) {
7424         PetscInt cStart = newPointOffsets[0][p];
7425         PetscInt b      = points[2 * p];
7426         PetscInt c, r, k;
7427         PetscInt dof;
7428 
7429         PetscCall(PetscSectionGetDof(section, b, &dof));
7430         if (!dof) continue;
7431         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7432           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7433           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7434 
7435           for (r = 0; r < numIndices; r++) {
7436             for (c = 0; c < nCols; c++) {
7437               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7438             }
7439           }
7440         } else {
7441           /* copy this column as is */
7442           for (r = 0; r < numIndices; r++) {
7443             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7444           }
7445         }
7446         oldOff += dof;
7447       }
7448     }
7449 
7450     if (multiplyLeft) {
7451       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7452       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7453       /* multiply constraints transpose on the left */
7454       if (numFields) {
7455         for (f = 0; f < numFields; f++) {
7456           PetscInt oldOff = offsets[f];
7457 
7458           for (p = 0; p < numPoints; p++) {
7459             PetscInt rStart = newPointOffsets[f][p];
7460             PetscInt b      = points[2 * p];
7461             PetscInt c, r, k;
7462             PetscInt dof;
7463 
7464             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7465             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7466               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7467               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7468 
7469               for (r = 0; r < nRows; r++) {
7470                 for (c = 0; c < newNumIndices; c++) {
7471                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7472                 }
7473               }
7474             } else {
7475               /* copy this row as is */
7476               for (r = 0; r < dof; r++) {
7477                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7478               }
7479             }
7480             oldOff += dof;
7481           }
7482         }
7483       } else {
7484         PetscInt oldOff = 0;
7485 
7486         for (p = 0; p < numPoints; p++) {
7487           PetscInt rStart = newPointOffsets[0][p];
7488           PetscInt b      = points[2 * p];
7489           PetscInt c, r, k;
7490           PetscInt dof;
7491 
7492           PetscCall(PetscSectionGetDof(section, b, &dof));
7493           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7494             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7495             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7496 
7497             for (r = 0; r < nRows; r++) {
7498               for (c = 0; c < newNumIndices; c++) {
7499                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7500               }
7501             }
7502           } else {
7503             /* copy this row as is */
7504             for (r = 0; r < dof; r++) {
7505               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7506             }
7507           }
7508           oldOff += dof;
7509         }
7510       }
7511 
7512       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7513     } else {
7514       newValues = tmpValues;
7515     }
7516   }
7517 
7518   /* clean up */
7519   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7520   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7521 
7522   if (numFields) {
7523     for (f = 0; f < numFields; f++) {
7524       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7525       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7526       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7527     }
7528   } else {
7529     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7530     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7531     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7532   }
7533   PetscCall(ISRestoreIndices(aIS, &anchors));
7534 
7535   /* output */
7536   if (outPoints) {
7537     *outPoints = newPoints;
7538   } else {
7539     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7540   }
7541   if (outValues) *outValues = newValues;
7542   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7543   PetscFunctionReturn(0);
7544 }
7545 
7546 /*@C
7547   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7548 
7549   Not collective
7550 
7551   Input Parameters:
7552 + dm         - The `DM`
7553 . section    - The `PetscSection` describing the points (a local section)
7554 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7555 . point      - The point defining the closure
7556 - useClPerm  - Use the closure point permutation if available
7557 
7558   Output Parameters:
7559 + numIndices - The number of dof indices in the closure of point with the input sections
7560 . indices    - The dof indices
7561 . outOffsets - Array to write the field offsets into, or NULL
7562 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7563 
7564   Level: advanced
7565 
7566   Notes:
7567   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7568 
7569   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7570   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7571   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7572   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7573   indices (with the above semantics) are implied.
7574 
7575 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7576           `PetscSection`, `DMGetGlobalSection()`
7577 @*/
7578 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7579 {
7580   /* Closure ordering */
7581   PetscSection    clSection;
7582   IS              clPoints;
7583   const PetscInt *clp;
7584   PetscInt       *points;
7585   const PetscInt *clperm = NULL;
7586   /* Dof permutation and sign flips */
7587   const PetscInt    **perms[32] = {NULL};
7588   const PetscScalar **flips[32] = {NULL};
7589   PetscScalar        *valCopy   = NULL;
7590   /* Hanging node constraints */
7591   PetscInt    *pointsC = NULL;
7592   PetscScalar *valuesC = NULL;
7593   PetscInt     NclC, NiC;
7594 
7595   PetscInt *idx;
7596   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7597   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7598 
7599   PetscFunctionBeginHot;
7600   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7601   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7602   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7603   if (numIndices) PetscValidIntPointer(numIndices, 6);
7604   if (indices) PetscValidPointer(indices, 7);
7605   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7606   if (values) PetscValidPointer(values, 9);
7607   PetscCall(PetscSectionGetNumFields(section, &Nf));
7608   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7609   PetscCall(PetscArrayzero(offsets, 32));
7610   /* 1) Get points in closure */
7611   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7612   if (useClPerm) {
7613     PetscInt depth, clsize;
7614     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7615     for (clsize = 0, p = 0; p < Ncl; p++) {
7616       PetscInt dof;
7617       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7618       clsize += dof;
7619     }
7620     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7621   }
7622   /* 2) Get number of indices on these points and field offsets from section */
7623   for (p = 0; p < Ncl * 2; p += 2) {
7624     PetscInt dof, fdof;
7625 
7626     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7627     for (f = 0; f < Nf; ++f) {
7628       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7629       offsets[f + 1] += fdof;
7630     }
7631     Ni += dof;
7632   }
7633   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7634   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7635   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7636   for (f = 0; f < PetscMax(1, Nf); ++f) {
7637     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7638     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7639     /* may need to apply sign changes to the element matrix */
7640     if (values && flips[f]) {
7641       PetscInt foffset = offsets[f];
7642 
7643       for (p = 0; p < Ncl; ++p) {
7644         PetscInt           pnt  = points[2 * p], fdof;
7645         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7646 
7647         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7648         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7649         if (flip) {
7650           PetscInt i, j, k;
7651 
7652           if (!valCopy) {
7653             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7654             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7655             *values = valCopy;
7656           }
7657           for (i = 0; i < fdof; ++i) {
7658             PetscScalar fval = flip[i];
7659 
7660             for (k = 0; k < Ni; ++k) {
7661               valCopy[Ni * (foffset + i) + k] *= fval;
7662               valCopy[Ni * k + (foffset + i)] *= fval;
7663             }
7664           }
7665         }
7666         foffset += fdof;
7667       }
7668     }
7669   }
7670   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7671   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7672   if (NclC) {
7673     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7674     for (f = 0; f < PetscMax(1, Nf); ++f) {
7675       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7676       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7677     }
7678     for (f = 0; f < PetscMax(1, Nf); ++f) {
7679       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7680       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7681     }
7682     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7683     Ncl    = NclC;
7684     Ni     = NiC;
7685     points = pointsC;
7686     if (values) *values = valuesC;
7687   }
7688   /* 5) Calculate indices */
7689   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7690   if (Nf) {
7691     PetscInt  idxOff;
7692     PetscBool useFieldOffsets;
7693 
7694     if (outOffsets) {
7695       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7696     }
7697     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7698     if (useFieldOffsets) {
7699       for (p = 0; p < Ncl; ++p) {
7700         const PetscInt pnt = points[p * 2];
7701 
7702         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7703       }
7704     } else {
7705       for (p = 0; p < Ncl; ++p) {
7706         const PetscInt pnt = points[p * 2];
7707 
7708         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7709         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7710          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7711          * global section. */
7712         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7713       }
7714     }
7715   } else {
7716     PetscInt off = 0, idxOff;
7717 
7718     for (p = 0; p < Ncl; ++p) {
7719       const PetscInt  pnt  = points[p * 2];
7720       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7721 
7722       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7723       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7724        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7725       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7726     }
7727   }
7728   /* 6) Cleanup */
7729   for (f = 0; f < PetscMax(1, Nf); ++f) {
7730     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7731     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7732   }
7733   if (NclC) {
7734     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7735   } else {
7736     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7737   }
7738 
7739   if (numIndices) *numIndices = Ni;
7740   if (indices) *indices = idx;
7741   PetscFunctionReturn(0);
7742 }
7743 
7744 /*@C
7745   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7746 
7747   Not collective
7748 
7749   Input Parameters:
7750 + dm         - The `DM`
7751 . section    - The `PetscSection` describing the points (a local section)
7752 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7753 . point      - The point defining the closure
7754 - useClPerm  - Use the closure point permutation if available
7755 
7756   Output Parameters:
7757 + numIndices - The number of dof indices in the closure of point with the input sections
7758 . indices    - The dof indices
7759 . outOffsets - Array to write the field offsets into, or NULL
7760 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7761 
7762   Level: advanced
7763 
7764   Notes:
7765   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7766 
7767   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7768   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7769   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7770   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7771   indices (with the above semantics) are implied.
7772 
7773 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7774 @*/
7775 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7776 {
7777   PetscFunctionBegin;
7778   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7779   PetscValidPointer(indices, 7);
7780   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7781   PetscFunctionReturn(0);
7782 }
7783 
7784 /*@C
7785   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7786 
7787   Not collective
7788 
7789   Input Parameters:
7790 + dm - The `DM`
7791 . section - The section describing the layout in v, or NULL to use the default section
7792 . globalSection - The section describing the layout in v, or NULL to use the default global section
7793 . A - The matrix
7794 . point - The point in the `DM`
7795 . values - The array of values
7796 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7797 
7798   Level: intermediate
7799 
7800 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7801 @*/
7802 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7803 {
7804   DM_Plex           *mesh = (DM_Plex *)dm->data;
7805   PetscInt          *indices;
7806   PetscInt           numIndices;
7807   const PetscScalar *valuesOrig = values;
7808   PetscErrorCode     ierr;
7809 
7810   PetscFunctionBegin;
7811   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7812   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7813   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7814   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7815   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7816   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7817 
7818   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7819 
7820   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7821   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7822   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7823   if (ierr) {
7824     PetscMPIInt rank;
7825 
7826     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7827     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7828     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7829     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7830     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7831     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7832   }
7833   if (mesh->printFEM > 1) {
7834     PetscInt i;
7835     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7836     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7837     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7838   }
7839 
7840   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7841   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7842   PetscFunctionReturn(0);
7843 }
7844 
7845 /*@C
7846   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7847 
7848   Not collective
7849 
7850   Input Parameters:
7851 + dmRow - The `DM` for the row fields
7852 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7853 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7854 . dmCol - The `DM` for the column fields
7855 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7856 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7857 . A - The matrix
7858 . point - The point in the `DM`
7859 . values - The array of values
7860 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7861 
7862   Level: intermediate
7863 
7864 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7865 @*/
7866 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7867 {
7868   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7869   PetscInt          *indicesRow, *indicesCol;
7870   PetscInt           numIndicesRow, numIndicesCol;
7871   const PetscScalar *valuesOrig = values;
7872   PetscErrorCode     ierr;
7873 
7874   PetscFunctionBegin;
7875   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7876   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7877   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7878   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7879   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7880   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7881   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7882   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7883   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7884   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7885   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7886 
7887   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7888   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7889 
7890   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7891   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7892   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7893   if (ierr) {
7894     PetscMPIInt rank;
7895 
7896     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7897     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7898     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7899     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7900     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7901     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7902   }
7903 
7904   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7905   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7906   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7907   PetscFunctionReturn(0);
7908 }
7909 
7910 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7911 {
7912   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7913   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7914   PetscInt       *cpoints = NULL;
7915   PetscInt       *findices, *cindices;
7916   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7917   PetscInt        foffsets[32], coffsets[32];
7918   DMPolytopeType  ct;
7919   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7920   PetscErrorCode  ierr;
7921 
7922   PetscFunctionBegin;
7923   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7924   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7925   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7926   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7927   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7928   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7929   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7930   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7931   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7932   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7933   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7934   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7935   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7936   PetscCall(PetscArrayzero(foffsets, 32));
7937   PetscCall(PetscArrayzero(coffsets, 32));
7938   /* Column indices */
7939   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7940   maxFPoints = numCPoints;
7941   /* Compress out points not in the section */
7942   /*   TODO: Squeeze out points with 0 dof as well */
7943   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7944   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7945     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7946       cpoints[q * 2]     = cpoints[p];
7947       cpoints[q * 2 + 1] = cpoints[p + 1];
7948       ++q;
7949     }
7950   }
7951   numCPoints = q;
7952   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7953     PetscInt fdof;
7954 
7955     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7956     if (!dof) continue;
7957     for (f = 0; f < numFields; ++f) {
7958       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7959       coffsets[f + 1] += fdof;
7960     }
7961     numCIndices += dof;
7962   }
7963   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7964   /* Row indices */
7965   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7966   {
7967     DMPlexTransform tr;
7968     DMPolytopeType *rct;
7969     PetscInt       *rsize, *rcone, *rornt, Nt;
7970 
7971     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7972     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7973     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7974     numSubcells = rsize[Nt - 1];
7975     PetscCall(DMPlexTransformDestroy(&tr));
7976   }
7977   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7978   for (r = 0, q = 0; r < numSubcells; ++r) {
7979     /* TODO Map from coarse to fine cells */
7980     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7981     /* Compress out points not in the section */
7982     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7983     for (p = 0; p < numFPoints * 2; p += 2) {
7984       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7985         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7986         if (!dof) continue;
7987         for (s = 0; s < q; ++s)
7988           if (fpoints[p] == ftotpoints[s * 2]) break;
7989         if (s < q) continue;
7990         ftotpoints[q * 2]     = fpoints[p];
7991         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7992         ++q;
7993       }
7994     }
7995     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7996   }
7997   numFPoints = q;
7998   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7999     PetscInt fdof;
8000 
8001     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8002     if (!dof) continue;
8003     for (f = 0; f < numFields; ++f) {
8004       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8005       foffsets[f + 1] += fdof;
8006     }
8007     numFIndices += dof;
8008   }
8009   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8010 
8011   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8012   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8013   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8014   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8015   if (numFields) {
8016     const PetscInt **permsF[32] = {NULL};
8017     const PetscInt **permsC[32] = {NULL};
8018 
8019     for (f = 0; f < numFields; f++) {
8020       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8021       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8022     }
8023     for (p = 0; p < numFPoints; p++) {
8024       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8025       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8026     }
8027     for (p = 0; p < numCPoints; p++) {
8028       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8029       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8030     }
8031     for (f = 0; f < numFields; f++) {
8032       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8033       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8034     }
8035   } else {
8036     const PetscInt **permsF = NULL;
8037     const PetscInt **permsC = NULL;
8038 
8039     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8040     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8041     for (p = 0, off = 0; p < numFPoints; p++) {
8042       const PetscInt *perm = permsF ? permsF[p] : NULL;
8043 
8044       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8045       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8046     }
8047     for (p = 0, off = 0; p < numCPoints; p++) {
8048       const PetscInt *perm = permsC ? permsC[p] : NULL;
8049 
8050       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8051       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8052     }
8053     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8054     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8055   }
8056   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8057   /* TODO: flips */
8058   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8059   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8060   if (ierr) {
8061     PetscMPIInt rank;
8062 
8063     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8064     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8065     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8066     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8067     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8068   }
8069   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8070   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8071   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8072   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8073   PetscFunctionReturn(0);
8074 }
8075 
8076 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8077 {
8078   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8079   PetscInt       *cpoints = NULL;
8080   PetscInt        foffsets[32], coffsets[32];
8081   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8082   DMPolytopeType  ct;
8083   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8084 
8085   PetscFunctionBegin;
8086   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8087   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8088   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8089   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8090   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8091   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8092   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8093   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8094   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8095   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8096   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8097   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8098   PetscCall(PetscArrayzero(foffsets, 32));
8099   PetscCall(PetscArrayzero(coffsets, 32));
8100   /* Column indices */
8101   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8102   maxFPoints = numCPoints;
8103   /* Compress out points not in the section */
8104   /*   TODO: Squeeze out points with 0 dof as well */
8105   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8106   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8107     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8108       cpoints[q * 2]     = cpoints[p];
8109       cpoints[q * 2 + 1] = cpoints[p + 1];
8110       ++q;
8111     }
8112   }
8113   numCPoints = q;
8114   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8115     PetscInt fdof;
8116 
8117     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8118     if (!dof) continue;
8119     for (f = 0; f < numFields; ++f) {
8120       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8121       coffsets[f + 1] += fdof;
8122     }
8123     numCIndices += dof;
8124   }
8125   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8126   /* Row indices */
8127   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8128   {
8129     DMPlexTransform tr;
8130     DMPolytopeType *rct;
8131     PetscInt       *rsize, *rcone, *rornt, Nt;
8132 
8133     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8134     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8135     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8136     numSubcells = rsize[Nt - 1];
8137     PetscCall(DMPlexTransformDestroy(&tr));
8138   }
8139   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8140   for (r = 0, q = 0; r < numSubcells; ++r) {
8141     /* TODO Map from coarse to fine cells */
8142     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8143     /* Compress out points not in the section */
8144     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8145     for (p = 0; p < numFPoints * 2; p += 2) {
8146       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8147         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8148         if (!dof) continue;
8149         for (s = 0; s < q; ++s)
8150           if (fpoints[p] == ftotpoints[s * 2]) break;
8151         if (s < q) continue;
8152         ftotpoints[q * 2]     = fpoints[p];
8153         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8154         ++q;
8155       }
8156     }
8157     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8158   }
8159   numFPoints = q;
8160   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8161     PetscInt fdof;
8162 
8163     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8164     if (!dof) continue;
8165     for (f = 0; f < numFields; ++f) {
8166       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8167       foffsets[f + 1] += fdof;
8168     }
8169     numFIndices += dof;
8170   }
8171   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8172 
8173   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8174   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8175   if (numFields) {
8176     const PetscInt **permsF[32] = {NULL};
8177     const PetscInt **permsC[32] = {NULL};
8178 
8179     for (f = 0; f < numFields; f++) {
8180       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8181       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8182     }
8183     for (p = 0; p < numFPoints; p++) {
8184       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8185       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8186     }
8187     for (p = 0; p < numCPoints; p++) {
8188       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8189       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8190     }
8191     for (f = 0; f < numFields; f++) {
8192       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8193       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8194     }
8195   } else {
8196     const PetscInt **permsF = NULL;
8197     const PetscInt **permsC = NULL;
8198 
8199     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8200     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8201     for (p = 0, off = 0; p < numFPoints; p++) {
8202       const PetscInt *perm = permsF ? permsF[p] : NULL;
8203 
8204       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8205       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8206     }
8207     for (p = 0, off = 0; p < numCPoints; p++) {
8208       const PetscInt *perm = permsC ? permsC[p] : NULL;
8209 
8210       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8211       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8212     }
8213     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8214     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8215   }
8216   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8217   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8218   PetscFunctionReturn(0);
8219 }
8220 
8221 /*@C
8222   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8223 
8224   Input Parameter:
8225 . dm   - The `DMPLEX` object
8226 
8227   Output Parameter:
8228 . cellHeight - The height of a cell
8229 
8230   Level: developer
8231 
8232 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`,  `DMPlexSetVTKCellHeight()`
8233 @*/
8234 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8235 {
8236   DM_Plex *mesh = (DM_Plex *)dm->data;
8237 
8238   PetscFunctionBegin;
8239   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8240   PetscValidIntPointer(cellHeight, 2);
8241   *cellHeight = mesh->vtkCellHeight;
8242   PetscFunctionReturn(0);
8243 }
8244 
8245 /*@C
8246   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8247 
8248   Input Parameters:
8249 + dm   - The `DMPLEX` object
8250 - cellHeight - The height of a cell
8251 
8252   Level: developer
8253 
8254 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8255 @*/
8256 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8257 {
8258   DM_Plex *mesh = (DM_Plex *)dm->data;
8259 
8260   PetscFunctionBegin;
8261   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8262   mesh->vtkCellHeight = cellHeight;
8263   PetscFunctionReturn(0);
8264 }
8265 
8266 /*@
8267   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8268 
8269   Input Parameter:
8270 . dm - The `DMPLEX` object
8271 
8272   Output Parameters:
8273 + gcStart - The first ghost cell, or NULL
8274 - gcEnd   - The upper bound on ghost cells, or NULL
8275 
8276   Level: advanced
8277 
8278 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8279 @*/
8280 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8281 {
8282   DMLabel ctLabel;
8283 
8284   PetscFunctionBegin;
8285   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8286   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8287   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8288   // Reset label for fast lookup
8289   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8290   PetscFunctionReturn(0);
8291 }
8292 
8293 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8294 {
8295   PetscSection section, globalSection;
8296   PetscInt    *numbers, p;
8297 
8298   PetscFunctionBegin;
8299   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8300   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8301   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8302   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8303   PetscCall(PetscSectionSetUp(section));
8304   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8305   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8306   for (p = pStart; p < pEnd; ++p) {
8307     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8308     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8309     else numbers[p - pStart] += shift;
8310   }
8311   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8312   if (globalSize) {
8313     PetscLayout layout;
8314     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8315     PetscCall(PetscLayoutGetSize(layout, globalSize));
8316     PetscCall(PetscLayoutDestroy(&layout));
8317   }
8318   PetscCall(PetscSectionDestroy(&section));
8319   PetscCall(PetscSectionDestroy(&globalSection));
8320   PetscFunctionReturn(0);
8321 }
8322 
8323 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8324 {
8325   PetscInt cellHeight, cStart, cEnd;
8326 
8327   PetscFunctionBegin;
8328   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8329   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8330   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8331   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8332   PetscFunctionReturn(0);
8333 }
8334 
8335 /*@
8336   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8337 
8338   Input Parameter:
8339 . dm   - The `DMPLEX` object
8340 
8341   Output Parameter:
8342 . globalCellNumbers - Global cell numbers for all cells on this process
8343 
8344   Level: developer
8345 
8346 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8347 @*/
8348 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8349 {
8350   DM_Plex *mesh = (DM_Plex *)dm->data;
8351 
8352   PetscFunctionBegin;
8353   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8354   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8355   *globalCellNumbers = mesh->globalCellNumbers;
8356   PetscFunctionReturn(0);
8357 }
8358 
8359 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8360 {
8361   PetscInt vStart, vEnd;
8362 
8363   PetscFunctionBegin;
8364   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8365   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8366   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8367   PetscFunctionReturn(0);
8368 }
8369 
8370 /*@
8371   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8372 
8373   Input Parameter:
8374 . dm   - The `DMPLEX` object
8375 
8376   Output Parameter:
8377 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8378 
8379   Level: developer
8380 
8381 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8382 @*/
8383 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8384 {
8385   DM_Plex *mesh = (DM_Plex *)dm->data;
8386 
8387   PetscFunctionBegin;
8388   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8389   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8390   *globalVertexNumbers = mesh->globalVertexNumbers;
8391   PetscFunctionReturn(0);
8392 }
8393 
8394 /*@
8395   DMPlexCreatePointNumbering - Create a global numbering for all points.
8396 
8397   Collective on dm
8398 
8399   Input Parameter:
8400 . dm   - The `DMPLEX` object
8401 
8402   Output Parameter:
8403 . globalPointNumbers - Global numbers for all points on this process
8404 
8405   Level: developer
8406 
8407   Notes:
8408   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8409   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8410   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8411   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8412 
8413   The partitioned mesh is
8414 ```
8415  (2)--0--(3)--1--(4)    (1)--0--(2)
8416 ```
8417   and its global numbering is
8418 ```
8419   (3)--0--(4)--1--(5)--2--(6)
8420 ```
8421   Then the global numbering is provided as
8422 ```
8423 [0] Number of indices in set 5
8424 [0] 0 0
8425 [0] 1 1
8426 [0] 2 3
8427 [0] 3 4
8428 [0] 4 -6
8429 [1] Number of indices in set 3
8430 [1] 0 2
8431 [1] 1 5
8432 [1] 2 6
8433 ```
8434 
8435 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8436 @*/
8437 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8438 {
8439   IS        nums[4];
8440   PetscInt  depths[4], gdepths[4], starts[4];
8441   PetscInt  depth, d, shift = 0;
8442   PetscBool empty = PETSC_FALSE;
8443 
8444   PetscFunctionBegin;
8445   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8446   PetscCall(DMPlexGetDepth(dm, &depth));
8447   // For unstratified meshes use dim instead of depth
8448   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8449   // If any stratum is empty, we must mark all empty
8450   for (d = 0; d <= depth; ++d) {
8451     PetscInt end;
8452 
8453     depths[d] = depth - d;
8454     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8455     if (!(starts[d] - end)) empty = PETSC_TRUE;
8456   }
8457   if (empty)
8458     for (d = 0; d <= depth; ++d) {
8459       depths[d] = -1;
8460       starts[d] = -1;
8461     }
8462   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8463   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8464   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]);
8465   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8466   for (d = 0; d <= depth; ++d) {
8467     PetscInt pStart, pEnd, gsize;
8468 
8469     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8470     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8471     shift += gsize;
8472   }
8473   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8474   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8475   PetscFunctionReturn(0);
8476 }
8477 
8478 /*@
8479   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8480 
8481   Input Parameter:
8482 . dm - The `DMPLEX` object
8483 
8484   Output Parameter:
8485 . ranks - The rank field
8486 
8487   Options Database Key:
8488 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8489 
8490   Level: intermediate
8491 
8492 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8493 @*/
8494 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8495 {
8496   DM             rdm;
8497   PetscFE        fe;
8498   PetscScalar   *r;
8499   PetscMPIInt    rank;
8500   DMPolytopeType ct;
8501   PetscInt       dim, cStart, cEnd, c;
8502   PetscBool      simplex;
8503 
8504   PetscFunctionBeginUser;
8505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8506   PetscValidPointer(ranks, 2);
8507   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8508   PetscCall(DMClone(dm, &rdm));
8509   PetscCall(DMGetDimension(rdm, &dim));
8510   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8511   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8512   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8513   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8514   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8515   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8516   PetscCall(PetscFEDestroy(&fe));
8517   PetscCall(DMCreateDS(rdm));
8518   PetscCall(DMCreateGlobalVector(rdm, ranks));
8519   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8520   PetscCall(VecGetArray(*ranks, &r));
8521   for (c = cStart; c < cEnd; ++c) {
8522     PetscScalar *lr;
8523 
8524     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8525     if (lr) *lr = rank;
8526   }
8527   PetscCall(VecRestoreArray(*ranks, &r));
8528   PetscCall(DMDestroy(&rdm));
8529   PetscFunctionReturn(0);
8530 }
8531 
8532 /*@
8533   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8534 
8535   Input Parameters:
8536 + dm    - The DMPlex
8537 - label - The DMLabel
8538 
8539   Output Parameter:
8540 . val - The label value field
8541 
8542   Options Database Keys:
8543 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8544 
8545   Level: intermediate
8546 
8547 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8548 @*/
8549 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8550 {
8551   DM           rdm;
8552   PetscFE      fe;
8553   PetscScalar *v;
8554   PetscInt     dim, cStart, cEnd, c;
8555 
8556   PetscFunctionBeginUser;
8557   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8558   PetscValidPointer(label, 2);
8559   PetscValidPointer(val, 3);
8560   PetscCall(DMClone(dm, &rdm));
8561   PetscCall(DMGetDimension(rdm, &dim));
8562   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8563   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8564   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8565   PetscCall(PetscFEDestroy(&fe));
8566   PetscCall(DMCreateDS(rdm));
8567   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8568   PetscCall(DMCreateGlobalVector(rdm, val));
8569   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8570   PetscCall(VecGetArray(*val, &v));
8571   for (c = cStart; c < cEnd; ++c) {
8572     PetscScalar *lv;
8573     PetscInt     cval;
8574 
8575     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8576     PetscCall(DMLabelGetValue(label, c, &cval));
8577     *lv = cval;
8578   }
8579   PetscCall(VecRestoreArray(*val, &v));
8580   PetscCall(DMDestroy(&rdm));
8581   PetscFunctionReturn(0);
8582 }
8583 
8584 /*@
8585   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8586 
8587   Input Parameter:
8588 . dm - The `DMPLEX` object
8589 
8590   Level: developer
8591 
8592   Notes:
8593   This is a useful diagnostic when creating meshes programmatically.
8594 
8595   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8596 
8597 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8598 @*/
8599 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8600 {
8601   PetscSection    coneSection, supportSection;
8602   const PetscInt *cone, *support;
8603   PetscInt        coneSize, c, supportSize, s;
8604   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8605   PetscBool       storagecheck = PETSC_TRUE;
8606 
8607   PetscFunctionBegin;
8608   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8609   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8610   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8611   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8612   /* Check that point p is found in the support of its cone points, and vice versa */
8613   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8614   for (p = pStart; p < pEnd; ++p) {
8615     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8616     PetscCall(DMPlexGetCone(dm, p, &cone));
8617     for (c = 0; c < coneSize; ++c) {
8618       PetscBool dup = PETSC_FALSE;
8619       PetscInt  d;
8620       for (d = c - 1; d >= 0; --d) {
8621         if (cone[c] == cone[d]) {
8622           dup = PETSC_TRUE;
8623           break;
8624         }
8625       }
8626       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8627       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8628       for (s = 0; s < supportSize; ++s) {
8629         if (support[s] == p) break;
8630       }
8631       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8632         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8633         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8634         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8635         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8636         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8637         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8638         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]);
8639         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8640       }
8641     }
8642     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8643     if (p != pp) {
8644       storagecheck = PETSC_FALSE;
8645       continue;
8646     }
8647     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8648     PetscCall(DMPlexGetSupport(dm, p, &support));
8649     for (s = 0; s < supportSize; ++s) {
8650       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8651       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8652       for (c = 0; c < coneSize; ++c) {
8653         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8654         if (cone[c] != pp) {
8655           c = 0;
8656           break;
8657         }
8658         if (cone[c] == p) break;
8659       }
8660       if (c >= coneSize) {
8661         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8662         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8663         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8664         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8665         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8666         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8667         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8668       }
8669     }
8670   }
8671   if (storagecheck) {
8672     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8673     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8674     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8675   }
8676   PetscFunctionReturn(0);
8677 }
8678 
8679 /*
8680   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.
8681 */
8682 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8683 {
8684   DMPolytopeType  cct;
8685   PetscInt        ptpoints[4];
8686   const PetscInt *cone, *ccone, *ptcone;
8687   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8688 
8689   PetscFunctionBegin;
8690   *unsplit = 0;
8691   switch (ct) {
8692   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8693     ptpoints[npt++] = c;
8694     break;
8695   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8696     PetscCall(DMPlexGetCone(dm, c, &cone));
8697     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8698     for (cp = 0; cp < coneSize; ++cp) {
8699       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8700       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8701     }
8702     break;
8703   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8704   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8705     PetscCall(DMPlexGetCone(dm, c, &cone));
8706     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8707     for (cp = 0; cp < coneSize; ++cp) {
8708       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8709       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8710       for (ccp = 0; ccp < cconeSize; ++ccp) {
8711         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8712         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8713           PetscInt p;
8714           for (p = 0; p < npt; ++p)
8715             if (ptpoints[p] == ccone[ccp]) break;
8716           if (p == npt) ptpoints[npt++] = ccone[ccp];
8717         }
8718       }
8719     }
8720     break;
8721   default:
8722     break;
8723   }
8724   for (pt = 0; pt < npt; ++pt) {
8725     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8726     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8727   }
8728   PetscFunctionReturn(0);
8729 }
8730 
8731 /*@
8732   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8733 
8734   Input Parameters:
8735 + dm - The `DMPLEX` object
8736 - cellHeight - Normally 0
8737 
8738   Level: developer
8739 
8740   Notes:
8741   This is a useful diagnostic when creating meshes programmatically.
8742   Currently applicable only to homogeneous simplex or tensor meshes.
8743 
8744   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8745 
8746 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8747 @*/
8748 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8749 {
8750   DMPlexInterpolatedFlag interp;
8751   DMPolytopeType         ct;
8752   PetscInt               vStart, vEnd, cStart, cEnd, c;
8753 
8754   PetscFunctionBegin;
8755   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8756   PetscCall(DMPlexIsInterpolated(dm, &interp));
8757   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8758   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8759   for (c = cStart; c < cEnd; ++c) {
8760     PetscInt *closure = NULL;
8761     PetscInt  coneSize, closureSize, cl, Nv = 0;
8762 
8763     PetscCall(DMPlexGetCellType(dm, c, &ct));
8764     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8765     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8766     if (interp == DMPLEX_INTERPOLATED_FULL) {
8767       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8768       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));
8769     }
8770     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8771     for (cl = 0; cl < closureSize * 2; cl += 2) {
8772       const PetscInt p = closure[cl];
8773       if ((p >= vStart) && (p < vEnd)) ++Nv;
8774     }
8775     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8776     /* Special Case: Tensor faces with identified vertices */
8777     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8778       PetscInt unsplit;
8779 
8780       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8781       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8782     }
8783     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));
8784   }
8785   PetscFunctionReturn(0);
8786 }
8787 
8788 /*@
8789   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8790 
8791   Collective on dm
8792 
8793   Input Parameters:
8794 + dm - The `DMPLEX` object
8795 - cellHeight - Normally 0
8796 
8797   Level: developer
8798 
8799   Notes:
8800   This is a useful diagnostic when creating meshes programmatically.
8801   This routine is only relevant for meshes that are fully interpolated across all ranks.
8802   It will error out if a partially interpolated mesh is given on some rank.
8803   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8804 
8805   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8806 
8807 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8808 @*/
8809 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8810 {
8811   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8812   DMPlexInterpolatedFlag interpEnum;
8813 
8814   PetscFunctionBegin;
8815   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8816   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8817   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8818   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8819     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8820     PetscFunctionReturn(0);
8821   }
8822 
8823   PetscCall(DMGetDimension(dm, &dim));
8824   PetscCall(DMPlexGetDepth(dm, &depth));
8825   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8826   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8827     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8828     for (c = cStart; c < cEnd; ++c) {
8829       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8830       const DMPolytopeType *faceTypes;
8831       DMPolytopeType        ct;
8832       PetscInt              numFaces, coneSize, f;
8833       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8834 
8835       PetscCall(DMPlexGetCellType(dm, c, &ct));
8836       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8837       if (unsplit) continue;
8838       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8839       PetscCall(DMPlexGetCone(dm, c, &cone));
8840       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8841       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8842       for (cl = 0; cl < closureSize * 2; cl += 2) {
8843         const PetscInt p = closure[cl];
8844         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8845       }
8846       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8847       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);
8848       for (f = 0; f < numFaces; ++f) {
8849         DMPolytopeType fct;
8850         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8851 
8852         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8853         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8854         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8855           const PetscInt p = fclosure[cl];
8856           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8857         }
8858         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]);
8859         for (v = 0; v < fnumCorners; ++v) {
8860           if (fclosure[v] != faces[fOff + v]) {
8861             PetscInt v1;
8862 
8863             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8864             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8865             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8866             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8867             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8868             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]);
8869           }
8870         }
8871         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8872         fOff += faceSizes[f];
8873       }
8874       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8875       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8876     }
8877   }
8878   PetscFunctionReturn(0);
8879 }
8880 
8881 /*@
8882   DMPlexCheckGeometry - Check the geometry of mesh cells
8883 
8884   Input Parameter:
8885 . dm - The `DMPLEX` object
8886 
8887   Level: developer
8888 
8889   Notes:
8890   This is a useful diagnostic when creating meshes programmatically.
8891 
8892   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8893 
8894 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8895 @*/
8896 PetscErrorCode DMPlexCheckGeometry(DM dm)
8897 {
8898   Vec       coordinates;
8899   PetscReal detJ, J[9], refVol = 1.0;
8900   PetscReal vol;
8901   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8902 
8903   PetscFunctionBegin;
8904   PetscCall(DMGetDimension(dm, &dim));
8905   PetscCall(DMGetCoordinateDim(dm, &dE));
8906   if (dim != dE) PetscFunctionReturn(0);
8907   PetscCall(DMPlexGetDepth(dm, &depth));
8908   for (d = 0; d < dim; ++d) refVol *= 2.0;
8909   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8910   /* Make sure local coordinates are created, because that step is collective */
8911   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8912   if (!coordinates) PetscFunctionReturn(0);
8913   for (c = cStart; c < cEnd; ++c) {
8914     DMPolytopeType ct;
8915     PetscInt       unsplit;
8916     PetscBool      ignoreZeroVol = PETSC_FALSE;
8917 
8918     PetscCall(DMPlexGetCellType(dm, c, &ct));
8919     switch (ct) {
8920     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8921     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8922     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8923       ignoreZeroVol = PETSC_TRUE;
8924       break;
8925     default:
8926       break;
8927     }
8928     switch (ct) {
8929     case DM_POLYTOPE_TRI_PRISM:
8930     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8931     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8932     case DM_POLYTOPE_PYRAMID:
8933       continue;
8934     default:
8935       break;
8936     }
8937     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8938     if (unsplit) continue;
8939     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8940     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);
8941     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8942     /* This should work with periodicity since DG coordinates should be used */
8943     if (depth > 1) {
8944       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8945       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);
8946       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8947     }
8948   }
8949   PetscFunctionReturn(0);
8950 }
8951 
8952 /*@
8953   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8954 
8955   Collective on dm
8956 
8957   Input Parameters:
8958 + dm - The `DMPLEX` object
8959 . pointSF - The `PetscSF`, or NULL for `PointSF` attached to `DM`
8960 - allowExtraRoots - Flag to allow extra points not present in the `DM`
8961 
8962   Level: developer
8963 
8964   Notes:
8965   This is mainly intended for debugging/testing purposes.
8966 
8967   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8968 
8969   Extra roots can come from priodic cuts, where additional points appear on the boundary
8970 
8971 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
8972 @*/
8973 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
8974 {
8975   PetscInt           l, nleaves, nroots, overlap;
8976   const PetscInt    *locals;
8977   const PetscSFNode *remotes;
8978   PetscBool          distributed;
8979   MPI_Comm           comm;
8980   PetscMPIInt        rank;
8981 
8982   PetscFunctionBegin;
8983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8984   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8985   else pointSF = dm->sf;
8986   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8987   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8988   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8989   {
8990     PetscMPIInt mpiFlag;
8991 
8992     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8993     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8994   }
8995   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8996   PetscCall(DMPlexIsDistributed(dm, &distributed));
8997   if (!distributed) {
8998     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);
8999     PetscFunctionReturn(0);
9000   }
9001   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);
9002   PetscCall(DMPlexGetOverlap(dm, &overlap));
9003 
9004   /* Check SF graph is compatible with DMPlex chart */
9005   {
9006     PetscInt pStart, pEnd, maxLeaf;
9007 
9008     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9009     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9010     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9011     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9012   }
9013 
9014   /* Check Point SF has no local points referenced */
9015   for (l = 0; l < nleaves; l++) {
9016     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);
9017   }
9018 
9019   /* Check there are no cells in interface */
9020   if (!overlap) {
9021     PetscInt cellHeight, cStart, cEnd;
9022 
9023     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9024     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9025     for (l = 0; l < nleaves; ++l) {
9026       const PetscInt point = locals ? locals[l] : l;
9027 
9028       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9029     }
9030   }
9031 
9032   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9033   {
9034     const PetscInt *rootdegree;
9035 
9036     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9037     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9038     for (l = 0; l < nleaves; ++l) {
9039       const PetscInt  point = locals ? locals[l] : l;
9040       const PetscInt *cone;
9041       PetscInt        coneSize, c, idx;
9042 
9043       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9044       PetscCall(DMPlexGetCone(dm, point, &cone));
9045       for (c = 0; c < coneSize; ++c) {
9046         if (!rootdegree[cone[c]]) {
9047           if (locals) {
9048             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9049           } else {
9050             idx = (cone[c] < nleaves) ? cone[c] : -1;
9051           }
9052           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9053         }
9054       }
9055     }
9056   }
9057   PetscFunctionReturn(0);
9058 }
9059 
9060 /*@
9061   DMPlexCheck - Perform various checks of Plex sanity
9062 
9063   Input Parameter:
9064 . dm - The `DMPLEX` object
9065 
9066   Level: developer
9067 
9068   Notes:
9069   This is a useful diagnostic when creating meshes programmatically.
9070 
9071   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
9072 
9073   Currently does not include DMPlexCheckCellShape().
9074 
9075 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, DMCreate(), DMSetFromOptions()
9076 @*/
9077 PetscErrorCode DMPlexCheck(DM dm)
9078 {
9079   PetscInt cellHeight;
9080 
9081   PetscFunctionBegin;
9082   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9083   PetscCall(DMPlexCheckSymmetry(dm));
9084   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9085   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9086   PetscCall(DMPlexCheckGeometry(dm));
9087   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9088   PetscCall(DMPlexCheckInterfaceCones(dm));
9089   PetscFunctionReturn(0);
9090 }
9091 
9092 typedef struct cell_stats {
9093   PetscReal min, max, sum, squaresum;
9094   PetscInt  count;
9095 } cell_stats_t;
9096 
9097 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9098 {
9099   PetscInt i, N = *len;
9100 
9101   for (i = 0; i < N; i++) {
9102     cell_stats_t *A = (cell_stats_t *)a;
9103     cell_stats_t *B = (cell_stats_t *)b;
9104 
9105     B->min = PetscMin(A->min, B->min);
9106     B->max = PetscMax(A->max, B->max);
9107     B->sum += A->sum;
9108     B->squaresum += A->squaresum;
9109     B->count += A->count;
9110   }
9111 }
9112 
9113 /*@
9114   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9115 
9116   Collective on dm
9117 
9118   Input Parameters:
9119 + dm        - The `DMPLEX` object
9120 . output    - If true, statistics will be displayed on stdout
9121 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9122 
9123   Level: developer
9124 
9125   Notes:
9126   This is mainly intended for debugging/testing purposes.
9127 
9128   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9129 
9130 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9131 @*/
9132 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9133 {
9134   DM           dmCoarse;
9135   cell_stats_t stats, globalStats;
9136   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9137   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9138   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9139   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9140   PetscMPIInt  rank, size;
9141 
9142   PetscFunctionBegin;
9143   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9144   stats.min = PETSC_MAX_REAL;
9145   stats.max = PETSC_MIN_REAL;
9146   stats.sum = stats.squaresum = 0.;
9147   stats.count                 = 0;
9148 
9149   PetscCallMPI(MPI_Comm_size(comm, &size));
9150   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9151   PetscCall(DMGetCoordinateDim(dm, &cdim));
9152   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9153   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9154   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9155   for (c = cStart; c < cEnd; c++) {
9156     PetscInt  i;
9157     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9158 
9159     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9160     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9161     for (i = 0; i < PetscSqr(cdim); ++i) {
9162       frobJ += J[i] * J[i];
9163       frobInvJ += invJ[i] * invJ[i];
9164     }
9165     cond2 = frobJ * frobInvJ;
9166     cond  = PetscSqrtReal(cond2);
9167 
9168     stats.min = PetscMin(stats.min, cond);
9169     stats.max = PetscMax(stats.max, cond);
9170     stats.sum += cond;
9171     stats.squaresum += cond2;
9172     stats.count++;
9173     if (output && cond > limit) {
9174       PetscSection coordSection;
9175       Vec          coordsLocal;
9176       PetscScalar *coords = NULL;
9177       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9178 
9179       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9180       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9181       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9182       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9183       for (i = 0; i < Nv / cdim; ++i) {
9184         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9185         for (d = 0; d < cdim; ++d) {
9186           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9187           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9188         }
9189         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9190       }
9191       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9192       for (cl = 0; cl < clSize * 2; cl += 2) {
9193         const PetscInt edge = closure[cl];
9194 
9195         if ((edge >= eStart) && (edge < eEnd)) {
9196           PetscReal len;
9197 
9198           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9199           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9200         }
9201       }
9202       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9203       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9204     }
9205   }
9206   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9207 
9208   if (size > 1) {
9209     PetscMPIInt  blockLengths[2] = {4, 1};
9210     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9211     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9212     MPI_Op       statReduce;
9213 
9214     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9215     PetscCallMPI(MPI_Type_commit(&statType));
9216     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9217     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9218     PetscCallMPI(MPI_Op_free(&statReduce));
9219     PetscCallMPI(MPI_Type_free(&statType));
9220   } else {
9221     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9222   }
9223   if (rank == 0) {
9224     count = globalStats.count;
9225     min   = globalStats.min;
9226     max   = globalStats.max;
9227     mean  = globalStats.sum / globalStats.count;
9228     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9229   }
9230 
9231   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));
9232   PetscCall(PetscFree2(J, invJ));
9233 
9234   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9235   if (dmCoarse) {
9236     PetscBool isplex;
9237 
9238     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9239     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9240   }
9241   PetscFunctionReturn(0);
9242 }
9243 
9244 /*@
9245   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9246   orthogonal quality below given tolerance.
9247 
9248   Collective on dm
9249 
9250   Input Parameters:
9251 + dm   - The `DMPLEX` object
9252 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9253 - atol - [0, 1] Absolute tolerance for tagging cells.
9254 
9255   Output Parameters:
9256 + OrthQual      - Vec containing orthogonal quality per cell
9257 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9258 
9259   Options Database Keys:
9260 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9261 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9262 
9263   Level: intermediate
9264 
9265   Notes:
9266   Orthogonal quality is given by the following formula:
9267 
9268   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9269 
9270   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
9271   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9272   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9273   calculating the cosine of the angle between these vectors.
9274 
9275   Orthogonal quality ranges from 1 (best) to 0 (worst).
9276 
9277   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9278   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9279 
9280   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9281 
9282 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9283 @*/
9284 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9285 {
9286   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9287   PetscInt              *idx;
9288   PetscScalar           *oqVals;
9289   const PetscScalar     *cellGeomArr, *faceGeomArr;
9290   PetscReal             *ci, *fi, *Ai;
9291   MPI_Comm               comm;
9292   Vec                    cellgeom, facegeom;
9293   DM                     dmFace, dmCell;
9294   IS                     glob;
9295   ISLocalToGlobalMapping ltog;
9296   PetscViewer            vwr;
9297 
9298   PetscFunctionBegin;
9299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9300   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9301   PetscValidPointer(OrthQual, 4);
9302   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9303   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9304   PetscCall(DMGetDimension(dm, &nc));
9305   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9306   {
9307     DMPlexInterpolatedFlag interpFlag;
9308 
9309     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9310     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9311       PetscMPIInt rank;
9312 
9313       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9314       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9315     }
9316   }
9317   if (OrthQualLabel) {
9318     PetscValidPointer(OrthQualLabel, 5);
9319     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9320     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9321   } else {
9322     *OrthQualLabel = NULL;
9323   }
9324   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9325   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9326   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9327   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9328   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9329   PetscCall(VecCreate(comm, OrthQual));
9330   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9331   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9332   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9333   PetscCall(VecSetUp(*OrthQual));
9334   PetscCall(ISDestroy(&glob));
9335   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9336   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9337   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9338   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9339   PetscCall(VecGetDM(cellgeom, &dmCell));
9340   PetscCall(VecGetDM(facegeom, &dmFace));
9341   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9342   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9343     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9344     PetscInt         cellarr[2], *adj = NULL;
9345     PetscScalar     *cArr, *fArr;
9346     PetscReal        minvalc = 1.0, minvalf = 1.0;
9347     PetscFVCellGeom *cg;
9348 
9349     idx[cellIter] = cell - cStart;
9350     cellarr[0]    = cell;
9351     /* Make indexing into cellGeom easier */
9352     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9353     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9354     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9355     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9356     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9357       PetscInt         i;
9358       const PetscInt   neigh  = adj[cellneigh];
9359       PetscReal        normci = 0, normfi = 0, normai = 0;
9360       PetscFVCellGeom *cgneigh;
9361       PetscFVFaceGeom *fg;
9362 
9363       /* Don't count ourselves in the neighbor list */
9364       if (neigh == cell) continue;
9365       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9366       cellarr[1] = neigh;
9367       {
9368         PetscInt        numcovpts;
9369         const PetscInt *covpts;
9370 
9371         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9372         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9373         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9374       }
9375 
9376       /* Compute c_i, f_i and their norms */
9377       for (i = 0; i < nc; i++) {
9378         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9379         fi[i] = fg->centroid[i] - cg->centroid[i];
9380         Ai[i] = fg->normal[i];
9381         normci += PetscPowReal(ci[i], 2);
9382         normfi += PetscPowReal(fi[i], 2);
9383         normai += PetscPowReal(Ai[i], 2);
9384       }
9385       normci = PetscSqrtReal(normci);
9386       normfi = PetscSqrtReal(normfi);
9387       normai = PetscSqrtReal(normai);
9388 
9389       /* Normalize and compute for each face-cell-normal pair */
9390       for (i = 0; i < nc; i++) {
9391         ci[i] = ci[i] / normci;
9392         fi[i] = fi[i] / normfi;
9393         Ai[i] = Ai[i] / normai;
9394         /* PetscAbs because I don't know if normals are guaranteed to point out */
9395         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9396         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9397       }
9398       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9399       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9400     }
9401     PetscCall(PetscFree(adj));
9402     PetscCall(PetscFree2(cArr, fArr));
9403     /* Defer to cell if they're equal */
9404     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9405     if (OrthQualLabel) {
9406       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9407     }
9408   }
9409   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9410   PetscCall(VecAssemblyBegin(*OrthQual));
9411   PetscCall(VecAssemblyEnd(*OrthQual));
9412   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9413   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9414   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9415   if (OrthQualLabel) {
9416     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9417   }
9418   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9419   PetscCall(PetscViewerDestroy(&vwr));
9420   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9421   PetscFunctionReturn(0);
9422 }
9423 
9424 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9425  * interpolator construction */
9426 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9427 {
9428   PetscSection section, newSection, gsection;
9429   PetscSF      sf;
9430   PetscBool    hasConstraints, ghasConstraints;
9431 
9432   PetscFunctionBegin;
9433   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9434   PetscValidPointer(odm, 2);
9435   PetscCall(DMGetLocalSection(dm, &section));
9436   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9437   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9438   if (!ghasConstraints) {
9439     PetscCall(PetscObjectReference((PetscObject)dm));
9440     *odm = dm;
9441     PetscFunctionReturn(0);
9442   }
9443   PetscCall(DMClone(dm, odm));
9444   PetscCall(DMCopyFields(dm, *odm));
9445   PetscCall(DMGetLocalSection(*odm, &newSection));
9446   PetscCall(DMGetPointSF(*odm, &sf));
9447   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9448   PetscCall(DMSetGlobalSection(*odm, gsection));
9449   PetscCall(PetscSectionDestroy(&gsection));
9450   PetscFunctionReturn(0);
9451 }
9452 
9453 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9454 {
9455   DM        dmco, dmfo;
9456   Mat       interpo;
9457   Vec       rscale;
9458   Vec       cglobalo, clocal;
9459   Vec       fglobal, fglobalo, flocal;
9460   PetscBool regular;
9461 
9462   PetscFunctionBegin;
9463   PetscCall(DMGetFullDM(dmc, &dmco));
9464   PetscCall(DMGetFullDM(dmf, &dmfo));
9465   PetscCall(DMSetCoarseDM(dmfo, dmco));
9466   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9467   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9468   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9469   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9470   PetscCall(DMCreateLocalVector(dmc, &clocal));
9471   PetscCall(VecSet(cglobalo, 0.));
9472   PetscCall(VecSet(clocal, 0.));
9473   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9474   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9475   PetscCall(DMCreateLocalVector(dmf, &flocal));
9476   PetscCall(VecSet(fglobal, 0.));
9477   PetscCall(VecSet(fglobalo, 0.));
9478   PetscCall(VecSet(flocal, 0.));
9479   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9480   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9481   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9482   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9483   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9484   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9485   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9486   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9487   *shift = fglobal;
9488   PetscCall(VecDestroy(&flocal));
9489   PetscCall(VecDestroy(&fglobalo));
9490   PetscCall(VecDestroy(&clocal));
9491   PetscCall(VecDestroy(&cglobalo));
9492   PetscCall(VecDestroy(&rscale));
9493   PetscCall(MatDestroy(&interpo));
9494   PetscCall(DMDestroy(&dmfo));
9495   PetscCall(DMDestroy(&dmco));
9496   PetscFunctionReturn(0);
9497 }
9498 
9499 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9500 {
9501   PetscObject shifto;
9502   Vec         shift;
9503 
9504   PetscFunctionBegin;
9505   if (!interp) {
9506     Vec rscale;
9507 
9508     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9509     PetscCall(VecDestroy(&rscale));
9510   } else {
9511     PetscCall(PetscObjectReference((PetscObject)interp));
9512   }
9513   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9514   if (!shifto) {
9515     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9516     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9517     shifto = (PetscObject)shift;
9518     PetscCall(VecDestroy(&shift));
9519   }
9520   shift = (Vec)shifto;
9521   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9522   PetscCall(VecAXPY(fineSol, 1.0, shift));
9523   PetscCall(MatDestroy(&interp));
9524   PetscFunctionReturn(0);
9525 }
9526 
9527 /* Pointwise interpolation
9528      Just code FEM for now
9529      u^f = I u^c
9530      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9531      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9532      I_{ij} = psi^f_i phi^c_j
9533 */
9534 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9535 {
9536   PetscSection gsc, gsf;
9537   PetscInt     m, n;
9538   void        *ctx;
9539   DM           cdm;
9540   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9541 
9542   PetscFunctionBegin;
9543   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9544   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9545   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9546   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9547 
9548   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9549   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9550   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9551   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9552   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9553 
9554   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9555   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9556   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9557   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9558   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9559   if (scaling) {
9560     /* Use naive scaling */
9561     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9562   }
9563   PetscFunctionReturn(0);
9564 }
9565 
9566 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9567 {
9568   VecScatter ctx;
9569 
9570   PetscFunctionBegin;
9571   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9572   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9573   PetscCall(VecScatterDestroy(&ctx));
9574   PetscFunctionReturn(0);
9575 }
9576 
9577 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[])
9578 {
9579   const PetscInt Nc = uOff[1] - uOff[0];
9580   PetscInt       c;
9581   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9582 }
9583 
9584 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9585 {
9586   DM           dmc;
9587   PetscDS      ds;
9588   Vec          ones, locmass;
9589   IS           cellIS;
9590   PetscFormKey key;
9591   PetscInt     depth;
9592 
9593   PetscFunctionBegin;
9594   PetscCall(DMClone(dm, &dmc));
9595   PetscCall(DMCopyDisc(dm, dmc));
9596   PetscCall(DMGetDS(dmc, &ds));
9597   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9598   PetscCall(DMCreateGlobalVector(dmc, mass));
9599   PetscCall(DMGetLocalVector(dmc, &ones));
9600   PetscCall(DMGetLocalVector(dmc, &locmass));
9601   PetscCall(DMPlexGetDepth(dmc, &depth));
9602   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9603   PetscCall(VecSet(locmass, 0.0));
9604   PetscCall(VecSet(ones, 1.0));
9605   key.label = NULL;
9606   key.value = 0;
9607   key.field = 0;
9608   key.part  = 0;
9609   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9610   PetscCall(ISDestroy(&cellIS));
9611   PetscCall(VecSet(*mass, 0.0));
9612   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9613   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9614   PetscCall(DMRestoreLocalVector(dmc, &ones));
9615   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9616   PetscCall(DMDestroy(&dmc));
9617   PetscFunctionReturn(0);
9618 }
9619 
9620 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9621 {
9622   PetscSection gsc, gsf;
9623   PetscInt     m, n;
9624   void        *ctx;
9625   DM           cdm;
9626   PetscBool    regular;
9627 
9628   PetscFunctionBegin;
9629   if (dmFine == dmCoarse) {
9630     DM            dmc;
9631     PetscDS       ds;
9632     PetscWeakForm wf;
9633     Vec           u;
9634     IS            cellIS;
9635     PetscFormKey  key;
9636     PetscInt      depth;
9637 
9638     PetscCall(DMClone(dmFine, &dmc));
9639     PetscCall(DMCopyDisc(dmFine, dmc));
9640     PetscCall(DMGetDS(dmc, &ds));
9641     PetscCall(PetscDSGetWeakForm(ds, &wf));
9642     PetscCall(PetscWeakFormClear(wf));
9643     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9644     PetscCall(DMCreateMatrix(dmc, mass));
9645     PetscCall(DMGetLocalVector(dmc, &u));
9646     PetscCall(DMPlexGetDepth(dmc, &depth));
9647     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9648     PetscCall(MatZeroEntries(*mass));
9649     key.label = NULL;
9650     key.value = 0;
9651     key.field = 0;
9652     key.part  = 0;
9653     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9654     PetscCall(ISDestroy(&cellIS));
9655     PetscCall(DMRestoreLocalVector(dmc, &u));
9656     PetscCall(DMDestroy(&dmc));
9657   } else {
9658     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9659     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9660     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9661     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9662 
9663     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9664     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9665     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9666     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9667 
9668     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9669     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9670     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9671     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9672   }
9673   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9674   PetscFunctionReturn(0);
9675 }
9676 
9677 /*@
9678   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9679 
9680   Input Parameter:
9681 . dm - The `DMPLEX` object
9682 
9683   Output Parameter:
9684 . regular - The flag
9685 
9686   Level: intermediate
9687 
9688 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9689 @*/
9690 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9691 {
9692   PetscFunctionBegin;
9693   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9694   PetscValidBoolPointer(regular, 2);
9695   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9696   PetscFunctionReturn(0);
9697 }
9698 
9699 /*@
9700   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9701 
9702   Input Parameters:
9703 + dm - The `DMPLEX` object
9704 - regular - The flag
9705 
9706   Level: intermediate
9707 
9708 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9709 @*/
9710 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9711 {
9712   PetscFunctionBegin;
9713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9714   ((DM_Plex *)dm->data)->regularRefinement = regular;
9715   PetscFunctionReturn(0);
9716 }
9717 
9718 /*@
9719   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9720   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9721 
9722   Not Collective
9723 
9724   Input Parameter:
9725 . dm - The `DMPLEX` object
9726 
9727   Output Parameters:
9728 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9729 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9730 
9731   Level: intermediate
9732 
9733 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9734 @*/
9735 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9736 {
9737   DM_Plex *plex = (DM_Plex *)dm->data;
9738 
9739   PetscFunctionBegin;
9740   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9741   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9742   if (anchorSection) *anchorSection = plex->anchorSection;
9743   if (anchorIS) *anchorIS = plex->anchorIS;
9744   PetscFunctionReturn(0);
9745 }
9746 
9747 /*@
9748   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9749   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9750   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9751 
9752   Collective on dm
9753 
9754   Input Parameters:
9755 + dm - The `DMPLEX` object
9756 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9757                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9758 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9759 
9760   Level: intermediate
9761 
9762   Notes:
9763   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9764   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9765 
9766   The reference counts of anchorSection and anchorIS are incremented.
9767 
9768 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9769 @*/
9770 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9771 {
9772   DM_Plex    *plex = (DM_Plex *)dm->data;
9773   PetscMPIInt result;
9774 
9775   PetscFunctionBegin;
9776   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9777   if (anchorSection) {
9778     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9779     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9780     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9781   }
9782   if (anchorIS) {
9783     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9784     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9785     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9786   }
9787 
9788   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9789   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9790   plex->anchorSection = anchorSection;
9791 
9792   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9793   PetscCall(ISDestroy(&plex->anchorIS));
9794   plex->anchorIS = anchorIS;
9795 
9796   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9797     PetscInt        size, a, pStart, pEnd;
9798     const PetscInt *anchors;
9799 
9800     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9801     PetscCall(ISGetLocalSize(anchorIS, &size));
9802     PetscCall(ISGetIndices(anchorIS, &anchors));
9803     for (a = 0; a < size; a++) {
9804       PetscInt p;
9805 
9806       p = anchors[a];
9807       if (p >= pStart && p < pEnd) {
9808         PetscInt dof;
9809 
9810         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9811         if (dof) {
9812           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9813           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9814         }
9815       }
9816     }
9817     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9818   }
9819   /* reset the generic constraints */
9820   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9821   PetscFunctionReturn(0);
9822 }
9823 
9824 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9825 {
9826   PetscSection anchorSection;
9827   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9828 
9829   PetscFunctionBegin;
9830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9831   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9832   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9833   PetscCall(PetscSectionGetNumFields(section, &numFields));
9834   if (numFields) {
9835     PetscInt f;
9836     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9837 
9838     for (f = 0; f < numFields; f++) {
9839       PetscInt numComp;
9840 
9841       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9842       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9843     }
9844   }
9845   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9846   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9847   pStart = PetscMax(pStart, sStart);
9848   pEnd   = PetscMin(pEnd, sEnd);
9849   pEnd   = PetscMax(pStart, pEnd);
9850   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9851   for (p = pStart; p < pEnd; p++) {
9852     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9853     if (dof) {
9854       PetscCall(PetscSectionGetDof(section, p, &dof));
9855       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9856       for (f = 0; f < numFields; f++) {
9857         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9858         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9859       }
9860     }
9861   }
9862   PetscCall(PetscSectionSetUp(*cSec));
9863   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9864   PetscFunctionReturn(0);
9865 }
9866 
9867 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9868 {
9869   PetscSection    aSec;
9870   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9871   const PetscInt *anchors;
9872   PetscInt        numFields, f;
9873   IS              aIS;
9874   MatType         mtype;
9875   PetscBool       iscuda, iskokkos;
9876 
9877   PetscFunctionBegin;
9878   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9879   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9880   PetscCall(PetscSectionGetStorageSize(section, &n));
9881   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9882   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9883   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9884   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9885   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9886   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9887   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9888   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9889   else mtype = MATSEQAIJ;
9890   PetscCall(MatSetType(*cMat, mtype));
9891   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9892   PetscCall(ISGetIndices(aIS, &anchors));
9893   /* cSec will be a subset of aSec and section */
9894   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9895   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9896   PetscCall(PetscMalloc1(m + 1, &i));
9897   i[0] = 0;
9898   PetscCall(PetscSectionGetNumFields(section, &numFields));
9899   for (p = pStart; p < pEnd; p++) {
9900     PetscInt rDof, rOff, r;
9901 
9902     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9903     if (!rDof) continue;
9904     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9905     if (numFields) {
9906       for (f = 0; f < numFields; f++) {
9907         annz = 0;
9908         for (r = 0; r < rDof; r++) {
9909           a = anchors[rOff + r];
9910           if (a < sStart || a >= sEnd) continue;
9911           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9912           annz += aDof;
9913         }
9914         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9915         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9916         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9917       }
9918     } else {
9919       annz = 0;
9920       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9921       for (q = 0; q < dof; q++) {
9922         a = anchors[rOff + q];
9923         if (a < sStart || a >= sEnd) continue;
9924         PetscCall(PetscSectionGetDof(section, a, &aDof));
9925         annz += aDof;
9926       }
9927       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9928       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9929       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9930     }
9931   }
9932   nnz = i[m];
9933   PetscCall(PetscMalloc1(nnz, &j));
9934   offset = 0;
9935   for (p = pStart; p < pEnd; p++) {
9936     if (numFields) {
9937       for (f = 0; f < numFields; f++) {
9938         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9939         for (q = 0; q < dof; q++) {
9940           PetscInt rDof, rOff, r;
9941           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9942           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9943           for (r = 0; r < rDof; r++) {
9944             PetscInt s;
9945 
9946             a = anchors[rOff + r];
9947             if (a < sStart || a >= sEnd) continue;
9948             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9949             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9950             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9951           }
9952         }
9953       }
9954     } else {
9955       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9956       for (q = 0; q < dof; q++) {
9957         PetscInt rDof, rOff, r;
9958         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9959         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9960         for (r = 0; r < rDof; r++) {
9961           PetscInt s;
9962 
9963           a = anchors[rOff + r];
9964           if (a < sStart || a >= sEnd) continue;
9965           PetscCall(PetscSectionGetDof(section, a, &aDof));
9966           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9967           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9968         }
9969       }
9970     }
9971   }
9972   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9973   PetscCall(PetscFree(i));
9974   PetscCall(PetscFree(j));
9975   PetscCall(ISRestoreIndices(aIS, &anchors));
9976   PetscFunctionReturn(0);
9977 }
9978 
9979 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9980 {
9981   DM_Plex     *plex = (DM_Plex *)dm->data;
9982   PetscSection anchorSection, section, cSec;
9983   Mat          cMat;
9984 
9985   PetscFunctionBegin;
9986   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9987   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9988   if (anchorSection) {
9989     PetscInt Nf;
9990 
9991     PetscCall(DMGetLocalSection(dm, &section));
9992     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9993     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9994     PetscCall(DMGetNumFields(dm, &Nf));
9995     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9996     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9997     PetscCall(PetscSectionDestroy(&cSec));
9998     PetscCall(MatDestroy(&cMat));
9999   }
10000   PetscFunctionReturn(0);
10001 }
10002 
10003 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10004 {
10005   IS           subis;
10006   PetscSection section, subsection;
10007 
10008   PetscFunctionBegin;
10009   PetscCall(DMGetLocalSection(dm, &section));
10010   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10011   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10012   /* Create subdomain */
10013   PetscCall(DMPlexFilter(dm, label, value, subdm));
10014   /* Create submodel */
10015   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10016   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10017   PetscCall(DMSetLocalSection(*subdm, subsection));
10018   PetscCall(PetscSectionDestroy(&subsection));
10019   PetscCall(DMCopyDisc(dm, *subdm));
10020   /* Create map from submodel to global model */
10021   if (is) {
10022     PetscSection    sectionGlobal, subsectionGlobal;
10023     IS              spIS;
10024     const PetscInt *spmap;
10025     PetscInt       *subIndices;
10026     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10027     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10028 
10029     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10030     PetscCall(ISGetIndices(spIS, &spmap));
10031     PetscCall(PetscSectionGetNumFields(section, &Nf));
10032     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10033     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10034     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10035     for (p = pStart; p < pEnd; ++p) {
10036       PetscInt gdof, pSubSize = 0;
10037 
10038       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10039       if (gdof > 0) {
10040         for (f = 0; f < Nf; ++f) {
10041           PetscInt fdof, fcdof;
10042 
10043           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10044           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10045           pSubSize += fdof - fcdof;
10046         }
10047         subSize += pSubSize;
10048         if (pSubSize) {
10049           if (bs < 0) {
10050             bs = pSubSize;
10051           } else if (bs != pSubSize) {
10052             /* Layout does not admit a pointwise block size */
10053             bs = 1;
10054           }
10055         }
10056       }
10057     }
10058     /* Must have same blocksize on all procs (some might have no points) */
10059     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10060     bsLocal[1] = bs;
10061     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10062     if (bsMinMax[0] != bsMinMax[1]) {
10063       bs = 1;
10064     } else {
10065       bs = bsMinMax[0];
10066     }
10067     PetscCall(PetscMalloc1(subSize, &subIndices));
10068     for (p = pStart; p < pEnd; ++p) {
10069       PetscInt gdof, goff;
10070 
10071       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10072       if (gdof > 0) {
10073         const PetscInt point = spmap[p];
10074 
10075         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10076         for (f = 0; f < Nf; ++f) {
10077           PetscInt fdof, fcdof, fc, f2, poff = 0;
10078 
10079           /* Can get rid of this loop by storing field information in the global section */
10080           for (f2 = 0; f2 < f; ++f2) {
10081             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10082             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10083             poff += fdof - fcdof;
10084           }
10085           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10086           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10087           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10088         }
10089       }
10090     }
10091     PetscCall(ISRestoreIndices(spIS, &spmap));
10092     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10093     if (bs > 1) {
10094       /* We need to check that the block size does not come from non-contiguous fields */
10095       PetscInt i, j, set = 1;
10096       for (i = 0; i < subSize; i += bs) {
10097         for (j = 0; j < bs; ++j) {
10098           if (subIndices[i + j] != subIndices[i] + j) {
10099             set = 0;
10100             break;
10101           }
10102         }
10103       }
10104       if (set) PetscCall(ISSetBlockSize(*is, bs));
10105     }
10106     /* Attach nullspace */
10107     for (f = 0; f < Nf; ++f) {
10108       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10109       if ((*subdm)->nullspaceConstructors[f]) break;
10110     }
10111     if (f < Nf) {
10112       MatNullSpace nullSpace;
10113       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10114 
10115       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10116       PetscCall(MatNullSpaceDestroy(&nullSpace));
10117     }
10118   }
10119   PetscFunctionReturn(0);
10120 }
10121 
10122 /*@
10123   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10124 
10125   Input Parameters:
10126 + dm - The `DM`
10127 - dummy - unused argument
10128 
10129   Options Database Key:
10130 . -dm_plex_monitor_throughput - Activate the monitor
10131 
10132   Level: developer
10133 
10134 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10135 @*/
10136 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10137 {
10138 #if defined(PETSC_USE_LOG)
10139   PetscStageLog      stageLog;
10140   PetscLogEvent      event;
10141   PetscLogStage      stage;
10142   PetscEventPerfInfo eventInfo;
10143   PetscReal          cellRate, flopRate;
10144   PetscInt           cStart, cEnd, Nf, N;
10145   const char        *name;
10146 #endif
10147 
10148   PetscFunctionBegin;
10149   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10150 #if defined(PETSC_USE_LOG)
10151   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10152   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10153   PetscCall(DMGetNumFields(dm, &Nf));
10154   PetscCall(PetscLogGetStageLog(&stageLog));
10155   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10156   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10157   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10158   N        = (cEnd - cStart) * Nf * eventInfo.count;
10159   flopRate = eventInfo.flops / eventInfo.time;
10160   cellRate = N / eventInfo.time;
10161   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)));
10162 #else
10163   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10164 #endif
10165   PetscFunctionReturn(0);
10166 }
10167