xref: /petsc/src/dm/impls/plex/plex.c (revision f2c6b1a247e0aba1e6cff92019aae48a2a13617a)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The `DMPLEX` object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(0);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(0);
49 }
50 
51 /*@
52   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
53 
54   Input Parameters:
55 + dm     - The `DMPLEX` object
56 - height - The cell height in the Plex, 0 is the default
57 
58   Output Parameters:
59 + cStart - The first "normal" cell
60 - cEnd   - The upper bound on "normal"" cells
61 
62   Level: developer
63 
64   Note:
65   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
66 
67 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(0);
106 }
107 
108 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
109 {
110   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
111   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
112 
113   PetscFunctionBegin;
114   *ft = PETSC_VTK_INVALID;
115   PetscCall(DMGetCoordinateDim(dm, &cdim));
116   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
118   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
119   if (field >= 0) {
120     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
121     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
122   } else {
123     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
124     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
125   }
126   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(0);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective on dm
154 
155   Input Parameters:
156 + dm - The `DMPLEX` object
157 . n  - The number of vectors
158 . u  - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(0);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrcpy(tmpname, vname));
202         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
203         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(0);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(0);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *coords, *array;
253   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(VecGetArrayRead(coordinates, &coords));
278   for (c = 0; c < N; c += dim) {
279     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
280     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
281     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
282     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
283   }
284   PetscCall(VecRestoreArrayRead(coordinates, &coords));
285   PetscCall(PetscDrawClear(draw));
286 
287   /* Could implement something like DMDASelectFields() */
288   for (f = 0; f < Nf; ++f) {
289     DM          fdm = dm;
290     Vec         fv  = v;
291     IS          fis;
292     char        prefix[PETSC_MAX_PATH_LEN];
293     const char *fname;
294 
295     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
296     PetscCall(PetscSectionGetFieldName(s, f, &fname));
297 
298     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
299     else prefix[0] = '\0';
300     if (Nf > 1) {
301       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
302       PetscCall(VecGetSubVector(v, fis, &fv));
303       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
304       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
305     }
306     for (comp = 0; comp < Nc; ++comp, ++w) {
307       PetscInt nmax = 2;
308 
309       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
310       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
311       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
312       PetscCall(PetscDrawSetTitle(draw, title));
313 
314       /* TODO Get max and min only for this component */
315       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
316       if (!flg) {
317         PetscCall(VecMin(fv, NULL, &vbound[0]));
318         PetscCall(VecMax(fv, NULL, &vbound[1]));
319         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
320       }
321       PetscCall(PetscDrawGetPopup(draw, &popup));
322       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
323       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
324 
325       PetscCall(VecGetArrayRead(fv, &array));
326       for (c = cStart; c < cEnd; ++c) {
327         PetscScalar *coords = NULL, *a   = NULL;
328         PetscInt     numCoords, color[4] = {-1, -1, -1, -1};
329 
330         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
331         if (a) {
332           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
333           color[1] = color[2] = color[3] = color[0];
334         } else {
335           PetscScalar *vals = NULL;
336           PetscInt     numVals, va;
337 
338           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
339           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
340           switch (numVals / Nc) {
341           case 3: /* P1 Triangle */
342           case 4: /* P1 Quadrangle */
343             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
344             break;
345           case 6: /* P2 Triangle */
346           case 8: /* P2 Quadrangle */
347             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
348             break;
349           default:
350             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
351           }
352           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
353         }
354         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
355         switch (numCoords) {
356         case 6:
357         case 12: /* Localized triangle */
358           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
359           break;
360         case 8:
361         case 16: /* Localized quadrilateral */
362           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
363           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
364           break;
365         default:
366           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
367         }
368         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
369       }
370       PetscCall(VecRestoreArrayRead(fv, &array));
371       PetscCall(PetscDrawFlush(draw));
372       PetscCall(PetscDrawPause(draw));
373       PetscCall(PetscDrawSave(draw));
374     }
375     if (Nf > 1) {
376       PetscCall(VecRestoreSubVector(v, fis, &fv));
377       PetscCall(ISDestroy(&fis));
378       PetscCall(DMDestroy(&fdm));
379     }
380   }
381   PetscFunctionReturn(0);
382 }
383 
384 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
385 {
386   DM        dm;
387   PetscDraw draw;
388   PetscInt  dim;
389   PetscBool isnull;
390 
391   PetscFunctionBegin;
392   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
393   PetscCall(PetscDrawIsNull(draw, &isnull));
394   if (isnull) PetscFunctionReturn(0);
395 
396   PetscCall(VecGetDM(v, &dm));
397   PetscCall(DMGetCoordinateDim(dm, &dim));
398   switch (dim) {
399   case 1:
400     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
401     break;
402   case 2:
403     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
404     break;
405   default:
406     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
407   }
408   PetscFunctionReturn(0);
409 }
410 
411 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
412 {
413   DM                      dm;
414   Vec                     locv;
415   const char             *name;
416   PetscSection            section;
417   PetscInt                pStart, pEnd;
418   PetscInt                numFields;
419   PetscViewerVTKFieldType ft;
420 
421   PetscFunctionBegin;
422   PetscCall(VecGetDM(v, &dm));
423   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
424   PetscCall(PetscObjectGetName((PetscObject)v, &name));
425   PetscCall(PetscObjectSetName((PetscObject)locv, name));
426   PetscCall(VecCopy(v, locv));
427   PetscCall(DMGetLocalSection(dm, &section));
428   PetscCall(PetscSectionGetNumFields(section, &numFields));
429   if (!numFields) {
430     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
431     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
432   } else {
433     PetscInt f;
434 
435     for (f = 0; f < numFields; f++) {
436       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
437       if (ft == PETSC_VTK_INVALID) continue;
438       PetscCall(PetscObjectReference((PetscObject)locv));
439       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
440     }
441     PetscCall(VecDestroy(&locv));
442   }
443   PetscFunctionReturn(0);
444 }
445 
446 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
447 {
448   DM        dm;
449   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
450 
451   PetscFunctionBegin;
452   PetscCall(VecGetDM(v, &dm));
453   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
454   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
455   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
456   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
457   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
458   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
459   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
460     PetscInt    i, numFields;
461     PetscObject fe;
462     PetscBool   fem  = PETSC_FALSE;
463     Vec         locv = v;
464     const char *name;
465     PetscInt    step;
466     PetscReal   time;
467 
468     PetscCall(DMGetNumFields(dm, &numFields));
469     for (i = 0; i < numFields; i++) {
470       PetscCall(DMGetField(dm, i, NULL, &fe));
471       if (fe->classid == PETSCFE_CLASSID) {
472         fem = PETSC_TRUE;
473         break;
474       }
475     }
476     if (fem) {
477       PetscObject isZero;
478 
479       PetscCall(DMGetLocalVector(dm, &locv));
480       PetscCall(PetscObjectGetName((PetscObject)v, &name));
481       PetscCall(PetscObjectSetName((PetscObject)locv, name));
482       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
483       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
484       PetscCall(VecCopy(v, locv));
485       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
486       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
487     }
488     if (isvtk) {
489       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
490     } else if (ishdf5) {
491 #if defined(PETSC_HAVE_HDF5)
492       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
493 #else
494       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
495 #endif
496     } else if (isdraw) {
497       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
498     } else if (isglvis) {
499       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
500       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
501       PetscCall(VecView_GLVis(locv, viewer));
502     } else if (iscgns) {
503 #if defined(PETSC_HAVE_CGNS)
504       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
505 #else
506       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
507 #endif
508     }
509     if (fem) {
510       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
511       PetscCall(DMRestoreLocalVector(dm, &locv));
512     }
513   } else {
514     PetscBool isseq;
515 
516     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
517     if (isseq) PetscCall(VecView_Seq(v, viewer));
518     else PetscCall(VecView_MPI(v, viewer));
519   }
520   PetscFunctionReturn(0);
521 }
522 
523 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
524 {
525   DM        dm;
526   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
527 
528   PetscFunctionBegin;
529   PetscCall(VecGetDM(v, &dm));
530   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
532   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
533   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
534   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
535   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
536   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
537   if (isvtk || isdraw || isglvis || iscgns) {
538     Vec         locv;
539     PetscObject isZero;
540     const char *name;
541 
542     PetscCall(DMGetLocalVector(dm, &locv));
543     PetscCall(PetscObjectGetName((PetscObject)v, &name));
544     PetscCall(PetscObjectSetName((PetscObject)locv, name));
545     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
546     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
547     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
548     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
549     PetscCall(VecView_Plex_Local(locv, viewer));
550     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
551     PetscCall(DMRestoreLocalVector(dm, &locv));
552   } else if (ishdf5) {
553 #if defined(PETSC_HAVE_HDF5)
554     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
555 #else
556     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
557 #endif
558   } else if (isexodusii) {
559 #if defined(PETSC_HAVE_EXODUSII)
560     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
561 #else
562     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
563 #endif
564   } else {
565     PetscBool isseq;
566 
567     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
568     if (isseq) PetscCall(VecView_Seq(v, viewer));
569     else PetscCall(VecView_MPI(v, viewer));
570   }
571   PetscFunctionReturn(0);
572 }
573 
574 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
575 {
576   DM                dm;
577   MPI_Comm          comm;
578   PetscViewerFormat format;
579   Vec               v;
580   PetscBool         isvtk, ishdf5;
581 
582   PetscFunctionBegin;
583   PetscCall(VecGetDM(originalv, &dm));
584   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
585   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
586   PetscCall(PetscViewerGetFormat(viewer, &format));
587   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
588   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
589   if (format == PETSC_VIEWER_NATIVE) {
590     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
591     /* this need a better fix */
592     if (dm->useNatural) {
593       if (dm->sfNatural) {
594         const char *vecname;
595         PetscInt    n, nroots;
596 
597         PetscCall(VecGetLocalSize(originalv, &n));
598         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
599         if (n == nroots) {
600           PetscCall(DMGetGlobalVector(dm, &v));
601           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
602           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
603           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
604           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
605         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
606       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
607     } else v = originalv;
608   } else v = originalv;
609 
610   if (ishdf5) {
611 #if defined(PETSC_HAVE_HDF5)
612     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
613 #else
614     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
615 #endif
616   } else if (isvtk) {
617     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
618   } else {
619     PetscBool isseq;
620 
621     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
622     if (isseq) PetscCall(VecView_Seq(v, viewer));
623     else PetscCall(VecView_MPI(v, viewer));
624   }
625   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
626   PetscFunctionReturn(0);
627 }
628 
629 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
630 {
631   DM        dm;
632   PetscBool ishdf5;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
638   if (ishdf5) {
639     DM          dmBC;
640     Vec         gv;
641     const char *name;
642 
643     PetscCall(DMGetOutputDM(dm, &dmBC));
644     PetscCall(DMGetGlobalVector(dmBC, &gv));
645     PetscCall(PetscObjectGetName((PetscObject)v, &name));
646     PetscCall(PetscObjectSetName((PetscObject)gv, name));
647     PetscCall(VecLoad_Default(gv, viewer));
648     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
649     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
650     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
651   } else PetscCall(VecLoad_Default(v, viewer));
652   PetscFunctionReturn(0);
653 }
654 
655 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
656 {
657   DM        dm;
658   PetscBool ishdf5, isexodusii;
659 
660   PetscFunctionBegin;
661   PetscCall(VecGetDM(v, &dm));
662   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
663   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
664   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
665   if (ishdf5) {
666 #if defined(PETSC_HAVE_HDF5)
667     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
668 #else
669     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
670 #endif
671   } else if (isexodusii) {
672 #if defined(PETSC_HAVE_EXODUSII)
673     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
674 #else
675     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
676 #endif
677   } else PetscCall(VecLoad_Default(v, viewer));
678   PetscFunctionReturn(0);
679 }
680 
681 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
682 {
683   DM                dm;
684   PetscViewerFormat format;
685   PetscBool         ishdf5;
686 
687   PetscFunctionBegin;
688   PetscCall(VecGetDM(originalv, &dm));
689   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
690   PetscCall(PetscViewerGetFormat(viewer, &format));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
692   if (format == PETSC_VIEWER_NATIVE) {
693     if (dm->useNatural) {
694       if (dm->sfNatural) {
695         if (ishdf5) {
696 #if defined(PETSC_HAVE_HDF5)
697           Vec         v;
698           const char *vecname;
699 
700           PetscCall(DMGetGlobalVector(dm, &v));
701           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
702           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
703           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
704           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
705           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
706           PetscCall(DMRestoreGlobalVector(dm, &v));
707 #else
708           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
709 #endif
710         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
711       }
712     } else PetscCall(VecLoad_Default(originalv, viewer));
713   }
714   PetscFunctionReturn(0);
715 }
716 
717 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
718 {
719   PetscSection       coordSection;
720   Vec                coordinates;
721   DMLabel            depthLabel, celltypeLabel;
722   const char        *name[4];
723   const PetscScalar *a;
724   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
725 
726   PetscFunctionBegin;
727   PetscCall(DMGetDimension(dm, &dim));
728   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
729   PetscCall(DMGetCoordinateSection(dm, &coordSection));
730   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
731   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
732   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
733   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
734   PetscCall(VecGetArrayRead(coordinates, &a));
735   name[0]       = "vertex";
736   name[1]       = "edge";
737   name[dim - 1] = "face";
738   name[dim]     = "cell";
739   for (c = cStart; c < cEnd; ++c) {
740     PetscInt *closure = NULL;
741     PetscInt  closureSize, cl, ct;
742 
743     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
744     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
745     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
746     PetscCall(PetscViewerASCIIPushTab(viewer));
747     for (cl = 0; cl < closureSize * 2; cl += 2) {
748       PetscInt point = closure[cl], depth, dof, off, d, p;
749 
750       if ((point < pStart) || (point >= pEnd)) continue;
751       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
752       if (!dof) continue;
753       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
754       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
755       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
756       for (p = 0; p < dof / dim; ++p) {
757         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
758         for (d = 0; d < dim; ++d) {
759           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
760           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
761         }
762         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
763       }
764       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
765     }
766     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
767     PetscCall(PetscViewerASCIIPopTab(viewer));
768   }
769   PetscCall(VecRestoreArrayRead(coordinates, &a));
770   PetscFunctionReturn(0);
771 }
772 
773 typedef enum {
774   CS_CARTESIAN,
775   CS_POLAR,
776   CS_CYLINDRICAL,
777   CS_SPHERICAL
778 } CoordSystem;
779 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
780 
781 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
782 {
783   PetscInt i;
784 
785   PetscFunctionBegin;
786   if (dim > 3) {
787     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
788   } else {
789     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
790 
791     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
792     switch (cs) {
793     case CS_CARTESIAN:
794       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
795       break;
796     case CS_POLAR:
797       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       break;
801     case CS_CYLINDRICAL:
802       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
803       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
804       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
805       trcoords[2] = coords[2];
806       break;
807     case CS_SPHERICAL:
808       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
809       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
810       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
811       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
812       break;
813     }
814     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
815   }
816   PetscFunctionReturn(0);
817 }
818 
819 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
820 {
821   DM_Plex          *mesh = (DM_Plex *)dm->data;
822   DM                cdm, cdmCell;
823   PetscSection      coordSection, coordSectionCell;
824   Vec               coordinates, coordinatesCell;
825   PetscViewerFormat format;
826 
827   PetscFunctionBegin;
828   PetscCall(PetscViewerGetFormat(viewer, &format));
829   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
830     const char *name;
831     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
832     PetscInt    pStart, pEnd, p, numLabels, l;
833     PetscMPIInt rank, size;
834 
835     PetscCall(DMGetCoordinateDM(dm, &cdm));
836     PetscCall(DMGetCoordinateSection(dm, &coordSection));
837     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
838     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
839     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
840     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
841     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
842     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
843     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
844     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
845     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
846     PetscCall(DMGetDimension(dm, &dim));
847     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
848     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
849     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
850     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
851     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
852     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
853     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
854     for (p = pStart; p < pEnd; ++p) {
855       PetscInt dof, off, s;
856 
857       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
858       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
859       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
860     }
861     PetscCall(PetscViewerFlush(viewer));
862     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
863     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
864     for (p = pStart; p < pEnd; ++p) {
865       PetscInt dof, off, c;
866 
867       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
868       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
869       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
870     }
871     PetscCall(PetscViewerFlush(viewer));
872     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
873     if (coordSection && coordinates) {
874       CoordSystem        cs = CS_CARTESIAN;
875       const PetscScalar *array, *arrayCell = NULL;
876       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
877       PetscMPIInt        rank;
878       const char        *name;
879 
880       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
881       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
882       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
883       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
884       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
885       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
886       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
887       pStart = PetscMin(pvStart, pcStart);
888       pEnd   = PetscMax(pvEnd, pcEnd);
889       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
890       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
891       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
892       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
893 
894       PetscCall(VecGetArrayRead(coordinates, &array));
895       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
896       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
897       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
898       for (p = pStart; p < pEnd; ++p) {
899         PetscInt dof, off;
900 
901         if (p >= pvStart && p < pvEnd) {
902           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
903           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
904           if (dof) {
905             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
906             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
907             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
908           }
909         }
910         if (cdmCell && p >= pcStart && p < pcEnd) {
911           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
912           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
913           if (dof) {
914             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
915             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
916             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
917           }
918         }
919       }
920       PetscCall(PetscViewerFlush(viewer));
921       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
922       PetscCall(VecRestoreArrayRead(coordinates, &array));
923       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
924     }
925     PetscCall(DMGetNumLabels(dm, &numLabels));
926     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
927     for (l = 0; l < numLabels; ++l) {
928       DMLabel     label;
929       PetscBool   isdepth;
930       const char *name;
931 
932       PetscCall(DMGetLabelName(dm, l, &name));
933       PetscCall(PetscStrcmp(name, "depth", &isdepth));
934       if (isdepth) continue;
935       PetscCall(DMGetLabel(dm, name, &label));
936       PetscCall(DMLabelView(label, viewer));
937     }
938     if (size > 1) {
939       PetscSF sf;
940 
941       PetscCall(DMGetPointSF(dm, &sf));
942       PetscCall(PetscSFView(sf, viewer));
943     }
944     PetscCall(PetscViewerFlush(viewer));
945   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
946     const char  *name, *color;
947     const char  *defcolors[3]  = {"gray", "orange", "green"};
948     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
949     char         lname[PETSC_MAX_PATH_LEN];
950     PetscReal    scale      = 2.0;
951     PetscReal    tikzscale  = 1.0;
952     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
953     double       tcoords[3];
954     PetscScalar *coords;
955     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
956     PetscMPIInt  rank, size;
957     char       **names, **colors, **lcolors;
958     PetscBool    flg, lflg;
959     PetscBT      wp = NULL;
960     PetscInt     pEnd, pStart;
961 
962     PetscCall(DMGetCoordinateDM(dm, &cdm));
963     PetscCall(DMGetCoordinateSection(dm, &coordSection));
964     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
965     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
966     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
967     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
968     PetscCall(DMGetDimension(dm, &dim));
969     PetscCall(DMPlexGetDepth(dm, &depth));
970     PetscCall(DMGetNumLabels(dm, &numLabels));
971     numLabels  = PetscMax(numLabels, 10);
972     numColors  = 10;
973     numLColors = 10;
974     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
975     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
976     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
977     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
978     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
979     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
980     n = 4;
981     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
982     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
983     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
984     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
985     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
986     if (!useLabels) numLabels = 0;
987     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
988     if (!useColors) {
989       numColors = 3;
990       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
991     }
992     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
993     if (!useColors) {
994       numLColors = 4;
995       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
996     }
997     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
998     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
999     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1000     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1001     if (depth < dim) plotEdges = PETSC_FALSE;
1002     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1003 
1004     /* filter points with labelvalue != labeldefaultvalue */
1005     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1006     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1007     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1008     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1009     if (lflg) {
1010       DMLabel lbl;
1011 
1012       PetscCall(DMGetLabel(dm, lname, &lbl));
1013       if (lbl) {
1014         PetscInt val, defval;
1015 
1016         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1017         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1018         for (c = pStart; c < pEnd; c++) {
1019           PetscInt *closure = NULL;
1020           PetscInt  closureSize;
1021 
1022           PetscCall(DMLabelGetValue(lbl, c, &val));
1023           if (val == defval) continue;
1024 
1025           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1026           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1027           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1028         }
1029       }
1030     }
1031 
1032     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1033     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1034     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1035     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1036 \\documentclass[tikz]{standalone}\n\n\
1037 \\usepackage{pgflibraryshapes}\n\
1038 \\usetikzlibrary{backgrounds}\n\
1039 \\usetikzlibrary{arrows}\n\
1040 \\begin{document}\n"));
1041     if (size > 1) {
1042       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1043       for (p = 0; p < size; ++p) {
1044         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1045         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1046       }
1047       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1048     }
1049     if (drawHasse) {
1050       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1051 
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1060       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1061       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1062       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1063       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1064     }
1065     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1066 
1067     /* Plot vertices */
1068     PetscCall(VecGetArray(coordinates, &coords));
1069     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1070     for (v = vStart; v < vEnd; ++v) {
1071       PetscInt  off, dof, d;
1072       PetscBool isLabeled = PETSC_FALSE;
1073 
1074       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1075       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1076       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1077       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1078       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1079       for (d = 0; d < dof; ++d) {
1080         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1081         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1082       }
1083       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1084       if (dim == 3) {
1085         PetscReal tmp = tcoords[1];
1086         tcoords[1]    = tcoords[2];
1087         tcoords[2]    = -tmp;
1088       }
1089       for (d = 0; d < dof; ++d) {
1090         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1091         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1092       }
1093       if (drawHasse) color = colors[0 % numColors];
1094       else color = colors[rank % numColors];
1095       for (l = 0; l < numLabels; ++l) {
1096         PetscInt val;
1097         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1098         if (val >= 0) {
1099           color     = lcolors[l % numLColors];
1100           isLabeled = PETSC_TRUE;
1101           break;
1102         }
1103       }
1104       if (drawNumbers[0]) {
1105         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1106       } else if (drawColors[0]) {
1107         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1108       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1109     }
1110     PetscCall(VecRestoreArray(coordinates, &coords));
1111     PetscCall(PetscViewerFlush(viewer));
1112     /* Plot edges */
1113     if (plotEdges) {
1114       PetscCall(VecGetArray(coordinates, &coords));
1115       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1116       for (e = eStart; e < eEnd; ++e) {
1117         const PetscInt *cone;
1118         PetscInt        coneSize, offA, offB, dof, d;
1119 
1120         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1121         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1122         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1123         PetscCall(DMPlexGetCone(dm, e, &cone));
1124         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1125         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1126         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1127         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1128         for (d = 0; d < dof; ++d) {
1129           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1130           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1131         }
1132         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1133         if (dim == 3) {
1134           PetscReal tmp = tcoords[1];
1135           tcoords[1]    = tcoords[2];
1136           tcoords[2]    = -tmp;
1137         }
1138         for (d = 0; d < dof; ++d) {
1139           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1140           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1141         }
1142         if (drawHasse) color = colors[1 % numColors];
1143         else color = colors[rank % numColors];
1144         for (l = 0; l < numLabels; ++l) {
1145           PetscInt val;
1146           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1147           if (val >= 0) {
1148             color = lcolors[l % numLColors];
1149             break;
1150           }
1151         }
1152         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1153       }
1154       PetscCall(VecRestoreArray(coordinates, &coords));
1155       PetscCall(PetscViewerFlush(viewer));
1156       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1157     }
1158     /* Plot cells */
1159     if (dim == 3 || !drawNumbers[1]) {
1160       for (e = eStart; e < eEnd; ++e) {
1161         const PetscInt *cone;
1162 
1163         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1164         color = colors[rank % numColors];
1165         for (l = 0; l < numLabels; ++l) {
1166           PetscInt val;
1167           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1168           if (val >= 0) {
1169             color = lcolors[l % numLColors];
1170             break;
1171           }
1172         }
1173         PetscCall(DMPlexGetCone(dm, e, &cone));
1174         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1175       }
1176     } else {
1177       DMPolytopeType ct;
1178 
1179       /* Drawing a 2D polygon */
1180       for (c = cStart; c < cEnd; ++c) {
1181         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1182         PetscCall(DMPlexGetCellType(dm, c, &ct));
1183         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1184           const PetscInt *cone;
1185           PetscInt        coneSize, e;
1186 
1187           PetscCall(DMPlexGetCone(dm, c, &cone));
1188           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1189           for (e = 0; e < coneSize; ++e) {
1190             const PetscInt *econe;
1191 
1192             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1193             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1194           }
1195         } else {
1196           PetscInt *closure = NULL;
1197           PetscInt  closureSize, Nv = 0, v;
1198 
1199           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1200           for (p = 0; p < closureSize * 2; p += 2) {
1201             const PetscInt point = closure[p];
1202 
1203             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1204           }
1205           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1206           for (v = 0; v <= Nv; ++v) {
1207             const PetscInt vertex = closure[v % Nv];
1208 
1209             if (v > 0) {
1210               if (plotEdges) {
1211                 const PetscInt *edge;
1212                 PetscInt        endpoints[2], ne;
1213 
1214                 endpoints[0] = closure[v - 1];
1215                 endpoints[1] = vertex;
1216                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1217                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1218                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1219                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1220               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1221             }
1222             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1223           }
1224           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1225           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1226         }
1227       }
1228     }
1229     for (c = cStart; c < cEnd; ++c) {
1230       double             ccoords[3] = {0.0, 0.0, 0.0};
1231       PetscBool          isLabeled  = PETSC_FALSE;
1232       PetscScalar       *cellCoords = NULL;
1233       const PetscScalar *array;
1234       PetscInt           numCoords, cdim, d;
1235       PetscBool          isDG;
1236 
1237       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1238       PetscCall(DMGetCoordinateDim(dm, &cdim));
1239       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1240       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1241       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1242       for (p = 0; p < numCoords / cdim; ++p) {
1243         for (d = 0; d < cdim; ++d) {
1244           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1245           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1246         }
1247         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1248         if (cdim == 3) {
1249           PetscReal tmp = tcoords[1];
1250           tcoords[1]    = tcoords[2];
1251           tcoords[2]    = -tmp;
1252         }
1253         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1254       }
1255       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1256       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1257       for (d = 0; d < cdim; ++d) {
1258         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1259         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1260       }
1261       if (drawHasse) color = colors[depth % numColors];
1262       else color = colors[rank % numColors];
1263       for (l = 0; l < numLabels; ++l) {
1264         PetscInt val;
1265         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1266         if (val >= 0) {
1267           color     = lcolors[l % numLColors];
1268           isLabeled = PETSC_TRUE;
1269           break;
1270         }
1271       }
1272       if (drawNumbers[dim]) {
1273         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1274       } else if (drawColors[dim]) {
1275         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1276       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1277     }
1278     if (drawHasse) {
1279       color = colors[depth % numColors];
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1281       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1282       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1285 
1286       color = colors[1 % numColors];
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1288       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1289       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1292 
1293       color = colors[0 % numColors];
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1295       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1296       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1297       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1298       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1299 
1300       for (p = pStart; p < pEnd; ++p) {
1301         const PetscInt *cone;
1302         PetscInt        coneSize, cp;
1303 
1304         PetscCall(DMPlexGetCone(dm, p, &cone));
1305         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1306         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1307       }
1308     }
1309     PetscCall(PetscViewerFlush(viewer));
1310     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1311     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1312     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1313     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1314     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1315     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1316     PetscCall(PetscFree3(names, colors, lcolors));
1317     PetscCall(PetscBTDestroy(&wp));
1318   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1319     Vec                    cown, acown;
1320     VecScatter             sct;
1321     ISLocalToGlobalMapping g2l;
1322     IS                     gid, acis;
1323     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1324     MPI_Group              ggroup, ngroup;
1325     PetscScalar           *array, nid;
1326     const PetscInt        *idxs;
1327     PetscInt              *idxs2, *start, *adjacency, *work;
1328     PetscInt64             lm[3], gm[3];
1329     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1330     PetscMPIInt            d1, d2, rank;
1331 
1332     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1333     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1334 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1335     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1336 #endif
1337     if (ncomm != MPI_COMM_NULL) {
1338       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1339       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1340       d1 = 0;
1341       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1342       nid = d2;
1343       PetscCallMPI(MPI_Group_free(&ggroup));
1344       PetscCallMPI(MPI_Group_free(&ngroup));
1345       PetscCallMPI(MPI_Comm_free(&ncomm));
1346     } else nid = 0.0;
1347 
1348     /* Get connectivity */
1349     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1350     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1351 
1352     /* filter overlapped local cells */
1353     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1354     PetscCall(ISGetIndices(gid, &idxs));
1355     PetscCall(ISGetLocalSize(gid, &cum));
1356     PetscCall(PetscMalloc1(cum, &idxs2));
1357     for (c = cStart, cum = 0; c < cEnd; c++) {
1358       if (idxs[c - cStart] < 0) continue;
1359       idxs2[cum++] = idxs[c - cStart];
1360     }
1361     PetscCall(ISRestoreIndices(gid, &idxs));
1362     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1363     PetscCall(ISDestroy(&gid));
1364     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1365 
1366     /* support for node-aware cell locality */
1367     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1368     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1369     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1370     PetscCall(VecGetArray(cown, &array));
1371     for (c = 0; c < numVertices; c++) array[c] = nid;
1372     PetscCall(VecRestoreArray(cown, &array));
1373     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1374     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1375     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1376     PetscCall(ISDestroy(&acis));
1377     PetscCall(VecScatterDestroy(&sct));
1378     PetscCall(VecDestroy(&cown));
1379 
1380     /* compute edgeCut */
1381     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1382     PetscCall(PetscMalloc1(cum, &work));
1383     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1384     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1385     PetscCall(ISDestroy(&gid));
1386     PetscCall(VecGetArray(acown, &array));
1387     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1388       PetscInt totl;
1389 
1390       totl = start[c + 1] - start[c];
1391       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1392       for (i = 0; i < totl; i++) {
1393         if (work[i] < 0) {
1394           ect += 1;
1395           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1396         }
1397       }
1398     }
1399     PetscCall(PetscFree(work));
1400     PetscCall(VecRestoreArray(acown, &array));
1401     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1402     lm[1] = -numVertices;
1403     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1404     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1405     lm[0] = ect;                     /* edgeCut */
1406     lm[1] = ectn;                    /* node-aware edgeCut */
1407     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1408     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1409     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1410 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1411     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1412 #else
1413     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1414 #endif
1415     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1416     PetscCall(PetscFree(start));
1417     PetscCall(PetscFree(adjacency));
1418     PetscCall(VecDestroy(&acown));
1419   } else {
1420     const char    *name;
1421     PetscInt      *sizes, *hybsizes, *ghostsizes;
1422     PetscInt       locDepth, depth, cellHeight, dim, d;
1423     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1424     PetscInt       numLabels, l, maxSize = 17;
1425     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1426     MPI_Comm       comm;
1427     PetscMPIInt    size, rank;
1428 
1429     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1430     PetscCallMPI(MPI_Comm_size(comm, &size));
1431     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1432     PetscCall(DMGetDimension(dm, &dim));
1433     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1434     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1435     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1436     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1437     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1438     PetscCall(DMPlexGetDepth(dm, &locDepth));
1439     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1440     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1441     gcNum = gcEnd - gcStart;
1442     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1443     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1444     for (d = 0; d <= depth; d++) {
1445       PetscInt Nc[2] = {0, 0}, ict;
1446 
1447       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1448       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1449       ict = ct0;
1450       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1451       ct0 = (DMPolytopeType)ict;
1452       for (p = pStart; p < pEnd; ++p) {
1453         DMPolytopeType ct;
1454 
1455         PetscCall(DMPlexGetCellType(dm, p, &ct));
1456         if (ct == ct0) ++Nc[0];
1457         else ++Nc[1];
1458       }
1459       if (size < maxSize) {
1460         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1461         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1462         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1463         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1464         for (p = 0; p < size; ++p) {
1465           if (rank == 0) {
1466             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1467             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1468             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1469           }
1470         }
1471       } else {
1472         PetscInt locMinMax[2];
1473 
1474         locMinMax[0] = Nc[0] + Nc[1];
1475         locMinMax[1] = Nc[0] + Nc[1];
1476         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1477         locMinMax[0] = Nc[1];
1478         locMinMax[1] = Nc[1];
1479         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1480         if (d == depth) {
1481           locMinMax[0] = gcNum;
1482           locMinMax[1] = gcNum;
1483           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1484         }
1485         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1486         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1487         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1488         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1489       }
1490       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1491     }
1492     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1493     {
1494       const PetscReal *maxCell;
1495       const PetscReal *L;
1496       PetscBool        localized;
1497 
1498       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1499       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1500       if (L || localized) {
1501         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1502         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1503         if (L) {
1504           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1505           for (d = 0; d < dim; ++d) {
1506             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1507             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1508           }
1509           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1510         }
1511         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1512         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1513       }
1514     }
1515     PetscCall(DMGetNumLabels(dm, &numLabels));
1516     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1517     for (l = 0; l < numLabels; ++l) {
1518       DMLabel         label;
1519       const char     *name;
1520       IS              valueIS;
1521       const PetscInt *values;
1522       PetscInt        numValues, v;
1523 
1524       PetscCall(DMGetLabelName(dm, l, &name));
1525       PetscCall(DMGetLabel(dm, name, &label));
1526       PetscCall(DMLabelGetNumValues(label, &numValues));
1527       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1528       PetscCall(DMLabelGetValueIS(label, &valueIS));
1529       PetscCall(ISGetIndices(valueIS, &values));
1530       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1531       for (v = 0; v < numValues; ++v) {
1532         PetscInt size;
1533 
1534         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1535         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1536         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1537       }
1538       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1539       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1540       PetscCall(ISRestoreIndices(valueIS, &values));
1541       PetscCall(ISDestroy(&valueIS));
1542     }
1543     {
1544       char    **labelNames;
1545       PetscInt  Nl = numLabels;
1546       PetscBool flg;
1547 
1548       PetscCall(PetscMalloc1(Nl, &labelNames));
1549       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1550       for (l = 0; l < Nl; ++l) {
1551         DMLabel label;
1552 
1553         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1554         if (flg) {
1555           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1556           PetscCall(DMLabelView(label, viewer));
1557         }
1558         PetscCall(PetscFree(labelNames[l]));
1559       }
1560       PetscCall(PetscFree(labelNames));
1561     }
1562     /* If no fields are specified, people do not want to see adjacency */
1563     if (dm->Nf) {
1564       PetscInt f;
1565 
1566       for (f = 0; f < dm->Nf; ++f) {
1567         const char *name;
1568 
1569         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1570         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1571         PetscCall(PetscViewerASCIIPushTab(viewer));
1572         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1573         if (dm->fields[f].adjacency[0]) {
1574           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1575           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1576         } else {
1577           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1578           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1579         }
1580         PetscCall(PetscViewerASCIIPopTab(viewer));
1581       }
1582     }
1583     PetscCall(DMGetCoarseDM(dm, &cdm));
1584     if (cdm) {
1585       PetscCall(PetscViewerASCIIPushTab(viewer));
1586       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1587       PetscCall(DMPlexView_Ascii(cdm, viewer));
1588       PetscCall(PetscViewerASCIIPopTab(viewer));
1589     }
1590   }
1591   PetscFunctionReturn(0);
1592 }
1593 
1594 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1595 {
1596   DMPolytopeType ct;
1597   PetscMPIInt    rank;
1598   PetscInt       cdim;
1599 
1600   PetscFunctionBegin;
1601   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1602   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1603   PetscCall(DMGetCoordinateDim(dm, &cdim));
1604   switch (ct) {
1605   case DM_POLYTOPE_SEGMENT:
1606   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1607     switch (cdim) {
1608     case 1: {
1609       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1610       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1611 
1612       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1613       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1614       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1615     } break;
1616     case 2: {
1617       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1618       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1619       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1620 
1621       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1622       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1623       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1624     } break;
1625     default:
1626       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1627     }
1628     break;
1629   case DM_POLYTOPE_TRIANGLE:
1630     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1631     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1632     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1633     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1634     break;
1635   case DM_POLYTOPE_QUADRILATERAL:
1636     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1637     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1638     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1639     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1640     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1641     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1642     break;
1643   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1644     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1645     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1646     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1647     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1648     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1649     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1650     break;
1651   case DM_POLYTOPE_FV_GHOST:
1652     break;
1653   default:
1654     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1655   }
1656   PetscFunctionReturn(0);
1657 }
1658 
1659 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1660 {
1661   DMPolytopeType ct;
1662   PetscReal      centroid[2] = {0., 0.};
1663   PetscMPIInt    rank;
1664   PetscInt       fillColor, v, e, d;
1665 
1666   PetscFunctionBegin;
1667   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1668   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1669   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1670   switch (ct) {
1671   case DM_POLYTOPE_TRIANGLE: {
1672     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1673 
1674     for (v = 0; v < 3; ++v) {
1675       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1676       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1677     }
1678     for (e = 0; e < 3; ++e) {
1679       refCoords[0] = refVertices[e * 2 + 0];
1680       refCoords[1] = refVertices[e * 2 + 1];
1681       for (d = 1; d <= edgeDiv; ++d) {
1682         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1683         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1684       }
1685       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1686       for (d = 0; d < edgeDiv; ++d) {
1687         PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1688         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1689       }
1690     }
1691   } break;
1692   default:
1693     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1694   }
1695   PetscFunctionReturn(0);
1696 }
1697 
1698 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1699 {
1700   PetscDraw          draw;
1701   DM                 cdm;
1702   PetscSection       coordSection;
1703   Vec                coordinates;
1704   const PetscScalar *coords;
1705   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1706   PetscReal         *refCoords, *edgeCoords;
1707   PetscBool          isnull, drawAffine = PETSC_TRUE;
1708   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1709 
1710   PetscFunctionBegin;
1711   PetscCall(DMGetCoordinateDim(dm, &dim));
1712   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1713   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1714   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1715   PetscCall(DMGetCoordinateDM(dm, &cdm));
1716   PetscCall(DMGetLocalSection(cdm, &coordSection));
1717   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1718   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1719   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1720 
1721   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1722   PetscCall(PetscDrawIsNull(draw, &isnull));
1723   if (isnull) PetscFunctionReturn(0);
1724   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1725 
1726   PetscCall(VecGetLocalSize(coordinates, &N));
1727   PetscCall(VecGetArrayRead(coordinates, &coords));
1728   for (c = 0; c < N; c += dim) {
1729     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1730     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1731     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1732     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1733   }
1734   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1735   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1736   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1737   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1738   PetscCall(PetscDrawClear(draw));
1739 
1740   for (c = cStart; c < cEnd; ++c) {
1741     PetscScalar *coords = NULL;
1742     PetscInt     numCoords;
1743 
1744     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1745     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1746     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1747     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1748   }
1749   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1750   PetscCall(PetscDrawFlush(draw));
1751   PetscCall(PetscDrawPause(draw));
1752   PetscCall(PetscDrawSave(draw));
1753   PetscFunctionReturn(0);
1754 }
1755 
1756 #if defined(PETSC_HAVE_EXODUSII)
1757   #include <exodusII.h>
1758   #include <petscviewerexodusii.h>
1759 #endif
1760 
1761 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1762 {
1763   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1764   char      name[PETSC_MAX_PATH_LEN];
1765 
1766   PetscFunctionBegin;
1767   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1768   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1769   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1770   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1771   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1772   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1773   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1774   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1775   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1776   if (iascii) {
1777     PetscViewerFormat format;
1778     PetscCall(PetscViewerGetFormat(viewer, &format));
1779     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1780     else PetscCall(DMPlexView_Ascii(dm, viewer));
1781   } else if (ishdf5) {
1782 #if defined(PETSC_HAVE_HDF5)
1783     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1784 #else
1785     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1786 #endif
1787   } else if (isvtk) {
1788     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1789   } else if (isdraw) {
1790     PetscCall(DMPlexView_Draw(dm, viewer));
1791   } else if (isglvis) {
1792     PetscCall(DMPlexView_GLVis(dm, viewer));
1793 #if defined(PETSC_HAVE_EXODUSII)
1794   } else if (isexodus) {
1795     /*
1796       exodusII requires that all sets be part of exactly one cell set.
1797       If the dm does not have a "Cell Sets" label defined, we create one
1798       with ID 1, containig all cells.
1799       Note that if the Cell Sets label is defined but does not cover all cells,
1800       we may still have a problem. This should probably be checked here or in the viewer;
1801     */
1802     PetscInt numCS;
1803     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1804     if (!numCS) {
1805       PetscInt cStart, cEnd, c;
1806       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1807       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1808       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1809     }
1810     PetscCall(DMView_PlexExodusII(dm, viewer));
1811 #endif
1812 #if defined(PETSC_HAVE_CGNS)
1813   } else if (iscgns) {
1814     PetscCall(DMView_PlexCGNS(dm, viewer));
1815 #endif
1816   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1817   /* Optionally view the partition */
1818   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1819   if (flg) {
1820     Vec ranks;
1821     PetscCall(DMPlexCreateRankField(dm, &ranks));
1822     PetscCall(VecView(ranks, viewer));
1823     PetscCall(VecDestroy(&ranks));
1824   }
1825   /* Optionally view a label */
1826   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1827   if (flg) {
1828     DMLabel label;
1829     Vec     val;
1830 
1831     PetscCall(DMGetLabel(dm, name, &label));
1832     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1833     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1834     PetscCall(VecView(val, viewer));
1835     PetscCall(VecDestroy(&val));
1836   }
1837   PetscFunctionReturn(0);
1838 }
1839 
1840 /*@
1841   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1842 
1843   Collective on dm
1844 
1845   Input Parameters:
1846 + dm     - The `DM` whose topology is to be saved
1847 - viewer - The `PetscViewer` to save it in
1848 
1849   Level: advanced
1850 
1851 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1852 @*/
1853 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1854 {
1855   PetscBool ishdf5;
1856 
1857   PetscFunctionBegin;
1858   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1859   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1860   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1861   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1862   if (ishdf5) {
1863 #if defined(PETSC_HAVE_HDF5)
1864     PetscViewerFormat format;
1865     PetscCall(PetscViewerGetFormat(viewer, &format));
1866     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1867       IS globalPointNumbering;
1868 
1869       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1870       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1871       PetscCall(ISDestroy(&globalPointNumbering));
1872     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1873 #else
1874     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1875 #endif
1876   }
1877   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1878   PetscFunctionReturn(0);
1879 }
1880 
1881 /*@
1882   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1883 
1884   Collective on dm
1885 
1886   Input Parameters:
1887 + dm     - The `DM` whose coordinates are to be saved
1888 - viewer - The `PetscViewer` for saving
1889 
1890   Level: advanced
1891 
1892 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1893 @*/
1894 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1895 {
1896   PetscBool ishdf5;
1897 
1898   PetscFunctionBegin;
1899   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1900   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1901   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1902   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1903   if (ishdf5) {
1904 #if defined(PETSC_HAVE_HDF5)
1905     PetscViewerFormat format;
1906     PetscCall(PetscViewerGetFormat(viewer, &format));
1907     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1908       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1909     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1910 #else
1911     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1912 #endif
1913   }
1914   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1915   PetscFunctionReturn(0);
1916 }
1917 
1918 /*@
1919   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1920 
1921   Collective on dm
1922 
1923   Input Parameters:
1924 + dm     - The `DM` whose labels are to be saved
1925 - viewer - The `PetscViewer` for saving
1926 
1927   Level: advanced
1928 
1929 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1930 @*/
1931 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1932 {
1933   PetscBool ishdf5;
1934 
1935   PetscFunctionBegin;
1936   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1937   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1938   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1939   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1940   if (ishdf5) {
1941 #if defined(PETSC_HAVE_HDF5)
1942     IS                globalPointNumbering;
1943     PetscViewerFormat format;
1944 
1945     PetscCall(PetscViewerGetFormat(viewer, &format));
1946     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1947       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1948       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1949       PetscCall(ISDestroy(&globalPointNumbering));
1950     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1951 #else
1952     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1953 #endif
1954   }
1955   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1956   PetscFunctionReturn(0);
1957 }
1958 
1959 /*@
1960   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1961 
1962   Collective on dm
1963 
1964   Input Parameters:
1965 + dm         - The `DM` that contains the topology on which the section to be saved is defined
1966 . viewer     - The `PetscViewer` for saving
1967 - sectiondm  - The `DM` that contains the section to be saved
1968 
1969   Level: advanced
1970 
1971   Notes:
1972   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
1973 
1974   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1975 
1976 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1977 @*/
1978 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1979 {
1980   PetscBool ishdf5;
1981 
1982   PetscFunctionBegin;
1983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1984   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1985   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1986   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1987   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1988   if (ishdf5) {
1989 #if defined(PETSC_HAVE_HDF5)
1990     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1991 #else
1992     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1993 #endif
1994   }
1995   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1996   PetscFunctionReturn(0);
1997 }
1998 
1999 /*@
2000   DMPlexGlobalVectorView - Saves a global vector
2001 
2002   Collective on dm
2003 
2004   Input Parameters:
2005 + dm        - The `DM` that represents the topology
2006 . viewer    - The `PetscViewer` to save data with
2007 . sectiondm - The `DM` that contains the global section on which vec is defined
2008 - vec       - The global vector to be saved
2009 
2010   Level: advanced
2011 
2012   Notes:
2013   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2014 
2015   Typical calling sequence:
2016 .vb
2017        DMCreate(PETSC_COMM_WORLD, &dm);
2018        DMSetType(dm, DMPLEX);
2019        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2020        DMClone(dm, &sectiondm);
2021        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2022        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2023        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2024        PetscSectionSetChart(section, pStart, pEnd);
2025        PetscSectionSetUp(section);
2026        DMSetLocalSection(sectiondm, section);
2027        PetscSectionDestroy(&section);
2028        DMGetGlobalVector(sectiondm, &vec);
2029        PetscObjectSetName((PetscObject)vec, "vec_name");
2030        DMPlexTopologyView(dm, viewer);
2031        DMPlexSectionView(dm, viewer, sectiondm);
2032        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2033        DMRestoreGlobalVector(sectiondm, &vec);
2034        DMDestroy(&sectiondm);
2035        DMDestroy(&dm);
2036 .ve
2037 
2038 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2039 @*/
2040 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2041 {
2042   PetscBool ishdf5;
2043 
2044   PetscFunctionBegin;
2045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2046   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2047   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2048   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2049   /* Check consistency */
2050   {
2051     PetscSection section;
2052     PetscBool    includesConstraints;
2053     PetscInt     m, m1;
2054 
2055     PetscCall(VecGetLocalSize(vec, &m1));
2056     PetscCall(DMGetGlobalSection(sectiondm, &section));
2057     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2058     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2059     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2060     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2061   }
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2063   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2064   if (ishdf5) {
2065 #if defined(PETSC_HAVE_HDF5)
2066     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2067 #else
2068     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2069 #endif
2070   }
2071   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2072   PetscFunctionReturn(0);
2073 }
2074 
2075 /*@
2076   DMPlexLocalVectorView - Saves a local vector
2077 
2078   Collective on dm
2079 
2080   Input Parameters:
2081 + dm        - The `DM` that represents the topology
2082 . viewer    - The `PetscViewer` to save data with
2083 . sectiondm - The `DM` that contains the local section on which vec is defined; may be the same as dm
2084 - vec       - The local vector to be saved
2085 
2086   Level: advanced
2087 
2088   Note:
2089   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2090 
2091   Typical calling sequence:
2092 .vb
2093        DMCreate(PETSC_COMM_WORLD, &dm);
2094        DMSetType(dm, DMPLEX);
2095        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2096        DMClone(dm, &sectiondm);
2097        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2098        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2099        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2100        PetscSectionSetChart(section, pStart, pEnd);
2101        PetscSectionSetUp(section);
2102        DMSetLocalSection(sectiondm, section);
2103        DMGetLocalVector(sectiondm, &vec);
2104        PetscObjectSetName((PetscObject)vec, "vec_name");
2105        DMPlexTopologyView(dm, viewer);
2106        DMPlexSectionView(dm, viewer, sectiondm);
2107        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2108        DMRestoreLocalVector(sectiondm, &vec);
2109        DMDestroy(&sectiondm);
2110        DMDestroy(&dm);
2111 .ve
2112 
2113 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2114 @*/
2115 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2116 {
2117   PetscBool ishdf5;
2118 
2119   PetscFunctionBegin;
2120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2121   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2122   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2123   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2124   /* Check consistency */
2125   {
2126     PetscSection section;
2127     PetscBool    includesConstraints;
2128     PetscInt     m, m1;
2129 
2130     PetscCall(VecGetLocalSize(vec, &m1));
2131     PetscCall(DMGetLocalSection(sectiondm, &section));
2132     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2133     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2134     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2135     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2136   }
2137   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2138   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2139   if (ishdf5) {
2140 #if defined(PETSC_HAVE_HDF5)
2141     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2142 #else
2143     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2144 #endif
2145   }
2146   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2147   PetscFunctionReturn(0);
2148 }
2149 
2150 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2151 {
2152   PetscBool ishdf5;
2153 
2154   PetscFunctionBegin;
2155   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2156   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2157   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2158   if (ishdf5) {
2159 #if defined(PETSC_HAVE_HDF5)
2160     PetscViewerFormat format;
2161     PetscCall(PetscViewerGetFormat(viewer, &format));
2162     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2163       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2164     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2165       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2166     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2167     PetscFunctionReturn(0);
2168 #else
2169     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2170 #endif
2171   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2172 }
2173 
2174 /*@
2175   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2176 
2177   Collective on dm
2178 
2179   Input Parameters:
2180 + dm                - The `DM` into which the topology is loaded
2181 - viewer            - The `PetscViewer` for the saved topology
2182 
2183   Output Parameters:
2184 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
2185 
2186   Level: advanced
2187 
2188 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2189           `PetscViewer`, `PetscSF`
2190 @*/
2191 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2192 {
2193   PetscBool ishdf5;
2194 
2195   PetscFunctionBegin;
2196   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2197   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2198   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2199   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2200   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2201   if (ishdf5) {
2202 #if defined(PETSC_HAVE_HDF5)
2203     PetscViewerFormat format;
2204     PetscCall(PetscViewerGetFormat(viewer, &format));
2205     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2206       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2207     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2208 #else
2209     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2210 #endif
2211   }
2212   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2213   PetscFunctionReturn(0);
2214 }
2215 
2216 /*@
2217   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2218 
2219   Collective on dm
2220 
2221   Input Parameters:
2222 + dm     - The `DM` into which the coordinates are loaded
2223 . viewer - The `PetscViewer` for the saved coordinates
2224 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2225 
2226   Level: advanced
2227 
2228 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2229           `PetscSF`, `PetscViewer`
2230 @*/
2231 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2232 {
2233   PetscBool ishdf5;
2234 
2235   PetscFunctionBegin;
2236   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2237   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2238   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2239   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2240   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2241   if (ishdf5) {
2242 #if defined(PETSC_HAVE_HDF5)
2243     PetscViewerFormat format;
2244     PetscCall(PetscViewerGetFormat(viewer, &format));
2245     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2246       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2247     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2248 #else
2249     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2250 #endif
2251   }
2252   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2253   PetscFunctionReturn(0);
2254 }
2255 
2256 /*@
2257   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2258 
2259   Collective on dm
2260 
2261   Input Parameters:
2262 + dm     - The `DM` into which the labels are loaded
2263 . viewer - The `PetscViewer` for the saved labels
2264 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2265 
2266   Level: advanced
2267 
2268   Note:
2269   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2270 
2271 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2272           `PetscSF`, `PetscViewer`
2273 @*/
2274 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2275 {
2276   PetscBool ishdf5;
2277 
2278   PetscFunctionBegin;
2279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2280   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2281   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2282   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2283   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2284   if (ishdf5) {
2285 #if defined(PETSC_HAVE_HDF5)
2286     PetscViewerFormat format;
2287 
2288     PetscCall(PetscViewerGetFormat(viewer, &format));
2289     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2290       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2291     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2292 #else
2293     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2294 #endif
2295   }
2296   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2297   PetscFunctionReturn(0);
2298 }
2299 
2300 /*@
2301   DMPlexSectionLoad - Loads section into a `DMPLEX`
2302 
2303   Collective on dm
2304 
2305   Input Parameters:
2306 + dm          - The `DM` that represents the topology
2307 . viewer      - The `PetscViewer` that represents the on-disk section (sectionA)
2308 . sectiondm   - The `DM` into which the on-disk section (sectionA) is migrated
2309 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2310 
2311   Output Parameters
2312 + globalDofSF - The SF that migrates any on-disk Vec data associated with sectionA into a global Vec associated with the sectiondm's global section (NULL if not needed)
2313 - localDofSF  - The SF that migrates any on-disk Vec data associated with sectionA into a local Vec associated with the sectiondm's local section (NULL if not needed)
2314 
2315   Level: advanced
2316 
2317   Notes:
2318   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in dm. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2319 
2320   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2321 
2322   The output parameter, globalDofSF (localDofSF), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2323 
2324   Example using 2 processes:
2325 .vb
2326   NX (number of points on dm): 4
2327   sectionA                   : the on-disk section
2328   vecA                       : a vector associated with sectionA
2329   sectionB                   : sectiondm's local section constructed in this function
2330   vecB (local)               : a vector associated with sectiondm's local section
2331   vecB (global)              : a vector associated with sectiondm's global section
2332 
2333                                      rank 0    rank 1
2334   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2335   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2336   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2337   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2338   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2339   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2340   sectionB->atlasDof             :     1 0 1 | 1 3
2341   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2342   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2343   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2344 .ve
2345   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2346 
2347 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2348 @*/
2349 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2350 {
2351   PetscBool ishdf5;
2352 
2353   PetscFunctionBegin;
2354   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2355   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2356   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2357   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2358   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2359   if (localDofSF) PetscValidPointer(localDofSF, 6);
2360   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2361   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2362   if (ishdf5) {
2363 #if defined(PETSC_HAVE_HDF5)
2364     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2365 #else
2366     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2367 #endif
2368   }
2369   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2370   PetscFunctionReturn(0);
2371 }
2372 
2373 /*@
2374   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2375 
2376   Collective on dm
2377 
2378   Input Parameters:
2379 + dm        - The `DM` that represents the topology
2380 . viewer    - The `PetscViewer` that represents the on-disk vector data
2381 . sectiondm - The `DM` that contains the global section on which vec is defined
2382 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2383 - vec       - The global vector to set values of
2384 
2385   Level: advanced
2386 
2387   Notes:
2388   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2389 
2390   Typical calling sequence:
2391 .vb
2392        DMCreate(PETSC_COMM_WORLD, &dm);
2393        DMSetType(dm, DMPLEX);
2394        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2395        DMPlexTopologyLoad(dm, viewer, &sfX);
2396        DMClone(dm, &sectiondm);
2397        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2398        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2399        DMGetGlobalVector(sectiondm, &vec);
2400        PetscObjectSetName((PetscObject)vec, "vec_name");
2401        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2402        DMRestoreGlobalVector(sectiondm, &vec);
2403        PetscSFDestroy(&gsf);
2404        PetscSFDestroy(&sfX);
2405        DMDestroy(&sectiondm);
2406        DMDestroy(&dm);
2407 .ve
2408 
2409 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2410           `PetscSF`, `PetscViewer`
2411 @*/
2412 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2413 {
2414   PetscBool ishdf5;
2415 
2416   PetscFunctionBegin;
2417   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2418   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2419   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2420   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2421   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2422   /* Check consistency */
2423   {
2424     PetscSection section;
2425     PetscBool    includesConstraints;
2426     PetscInt     m, m1;
2427 
2428     PetscCall(VecGetLocalSize(vec, &m1));
2429     PetscCall(DMGetGlobalSection(sectiondm, &section));
2430     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2431     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2432     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2433     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2434   }
2435   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2436   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2437   if (ishdf5) {
2438 #if defined(PETSC_HAVE_HDF5)
2439     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2440 #else
2441     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2442 #endif
2443   }
2444   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2445   PetscFunctionReturn(0);
2446 }
2447 
2448 /*@
2449   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2450 
2451   Collective on dm
2452 
2453   Input Parameters:
2454 + dm        - The `DM` that represents the topology
2455 . viewer    - The `PetscViewer` that represents the on-disk vector data
2456 . sectiondm - The `DM` that contains the local section on which vec is defined
2457 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2458 - vec       - The local vector to set values of
2459 
2460   Level: advanced
2461 
2462   Notes:
2463   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2464 
2465   Typical calling sequence:
2466 .vb
2467        DMCreate(PETSC_COMM_WORLD, &dm);
2468        DMSetType(dm, DMPLEX);
2469        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2470        DMPlexTopologyLoad(dm, viewer, &sfX);
2471        DMClone(dm, &sectiondm);
2472        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2473        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2474        DMGetLocalVector(sectiondm, &vec);
2475        PetscObjectSetName((PetscObject)vec, "vec_name");
2476        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2477        DMRestoreLocalVector(sectiondm, &vec);
2478        PetscSFDestroy(&lsf);
2479        PetscSFDestroy(&sfX);
2480        DMDestroy(&sectiondm);
2481        DMDestroy(&dm);
2482 .ve
2483 
2484 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2485           `PetscSF`, `PetscViewer`
2486 @*/
2487 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2488 {
2489   PetscBool ishdf5;
2490 
2491   PetscFunctionBegin;
2492   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2493   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2494   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2495   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2496   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2497   /* Check consistency */
2498   {
2499     PetscSection section;
2500     PetscBool    includesConstraints;
2501     PetscInt     m, m1;
2502 
2503     PetscCall(VecGetLocalSize(vec, &m1));
2504     PetscCall(DMGetLocalSection(sectiondm, &section));
2505     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2506     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2507     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2508     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2509   }
2510   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2511   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2512   if (ishdf5) {
2513 #if defined(PETSC_HAVE_HDF5)
2514     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2515 #else
2516     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2517 #endif
2518   }
2519   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2520   PetscFunctionReturn(0);
2521 }
2522 
2523 PetscErrorCode DMDestroy_Plex(DM dm)
2524 {
2525   DM_Plex *mesh = (DM_Plex *)dm->data;
2526 
2527   PetscFunctionBegin;
2528   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2529   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2530   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2531   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2532   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2533   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2534   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2535   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2536   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2537   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2538   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2539   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2540   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2541   if (--mesh->refct > 0) PetscFunctionReturn(0);
2542   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2543   PetscCall(PetscFree(mesh->cones));
2544   PetscCall(PetscFree(mesh->coneOrientations));
2545   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2546   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2547   PetscCall(PetscFree(mesh->supports));
2548   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2549   PetscCall(PetscFree(mesh->facesTmp));
2550   PetscCall(PetscFree(mesh->tetgenOpts));
2551   PetscCall(PetscFree(mesh->triangleOpts));
2552   PetscCall(PetscFree(mesh->transformType));
2553   PetscCall(PetscFree(mesh->distributionName));
2554   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2555   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2556   PetscCall(ISDestroy(&mesh->subpointIS));
2557   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2558   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2559   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2560   PetscCall(ISDestroy(&mesh->anchorIS));
2561   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2562   PetscCall(PetscFree(mesh->parents));
2563   PetscCall(PetscFree(mesh->childIDs));
2564   PetscCall(PetscSectionDestroy(&mesh->childSection));
2565   PetscCall(PetscFree(mesh->children));
2566   PetscCall(DMDestroy(&mesh->referenceTree));
2567   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2568   PetscCall(PetscFree(mesh->neighbors));
2569   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2570   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2571   PetscCall(PetscFree(mesh));
2572   PetscFunctionReturn(0);
2573 }
2574 
2575 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2576 {
2577   PetscSection           sectionGlobal;
2578   PetscInt               bs = -1, mbs;
2579   PetscInt               localSize, localStart = 0;
2580   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2581   MatType                mtype;
2582   ISLocalToGlobalMapping ltog;
2583 
2584   PetscFunctionBegin;
2585   PetscCall(MatInitializePackage());
2586   mtype = dm->mattype;
2587   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2588   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2589   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2590   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2591   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2592   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2593   PetscCall(MatSetType(*J, mtype));
2594   PetscCall(MatSetFromOptions(*J));
2595   PetscCall(MatGetBlockSize(*J, &mbs));
2596   if (mbs > 1) bs = mbs;
2597   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2598   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2599   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2600   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2601   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2602   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2603   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2604   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2605   if (!isShell) {
2606     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2607     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2608     PetscInt  pStart, pEnd, p, dof, cdof;
2609 
2610     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2611 
2612     PetscCall(PetscCalloc1(localSize, &pblocks));
2613     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2614     for (p = pStart; p < pEnd; ++p) {
2615       PetscInt bdof, offset;
2616 
2617       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2618       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2619       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2620       for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2621       dof  = dof < 0 ? -(dof + 1) : dof;
2622       bdof = cdof && (dof - cdof) ? 1 : dof;
2623       if (dof) {
2624         if (bs < 0) {
2625           bs = bdof;
2626         } else if (bs != bdof) {
2627           bs = 1;
2628         }
2629       }
2630     }
2631     /* Must have same blocksize on all procs (some might have no points) */
2632     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2633     bsLocal[1] = bs;
2634     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2635     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2636     else bs = bsMinMax[0];
2637     bs = PetscMax(1, bs);
2638     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2639     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2640       PetscCall(MatSetBlockSize(*J, bs));
2641       PetscCall(MatSetUp(*J));
2642     } else {
2643       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2644       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2645       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2646     }
2647     { // Consolidate blocks
2648       PetscInt nblocks = 0;
2649       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2650         if (pblocks[i] == 0) continue;
2651         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2652         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2653       }
2654       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2655     }
2656     PetscCall(PetscFree(pblocks));
2657   }
2658   PetscCall(MatSetDM(*J, dm));
2659   PetscFunctionReturn(0);
2660 }
2661 
2662 /*@
2663   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2664 
2665   Not Collective
2666 
2667   Input Parameter:
2668 . mesh - The `DMPLEX`
2669 
2670   Output Parameters:
2671 . subsection - The subdomain section
2672 
2673   Level: developer
2674 
2675 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `PetscSection`
2676 @*/
2677 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2678 {
2679   DM_Plex *mesh = (DM_Plex *)dm->data;
2680 
2681   PetscFunctionBegin;
2682   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2683   if (!mesh->subdomainSection) {
2684     PetscSection section;
2685     PetscSF      sf;
2686 
2687     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2688     PetscCall(DMGetLocalSection(dm, &section));
2689     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2690     PetscCall(PetscSFDestroy(&sf));
2691   }
2692   *subsection = mesh->subdomainSection;
2693   PetscFunctionReturn(0);
2694 }
2695 
2696 /*@
2697   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2698 
2699   Not Collective
2700 
2701   Input Parameter:
2702 . mesh - The `DMPLEX`
2703 
2704   Output Parameters:
2705 + pStart - The first mesh point
2706 - pEnd   - The upper bound for mesh points
2707 
2708   Level: beginner
2709 
2710 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2711 @*/
2712 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2713 {
2714   DM_Plex *mesh = (DM_Plex *)dm->data;
2715 
2716   PetscFunctionBegin;
2717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2718   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2719   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2720   PetscFunctionReturn(0);
2721 }
2722 
2723 /*@
2724   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2725 
2726   Not Collective
2727 
2728   Input Parameters:
2729 + mesh - The `DMPLEX`
2730 . pStart - The first mesh point
2731 - pEnd   - The upper bound for mesh points
2732 
2733   Level: beginner
2734 
2735 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2736 @*/
2737 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2738 {
2739   DM_Plex *mesh = (DM_Plex *)dm->data;
2740 
2741   PetscFunctionBegin;
2742   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2743   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2744   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2745   PetscFunctionReturn(0);
2746 }
2747 
2748 /*@
2749   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2750 
2751   Not Collective
2752 
2753   Input Parameters:
2754 + mesh - The `DMPLEX`
2755 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2756 
2757   Output Parameter:
2758 . size - The cone size for point p
2759 
2760   Level: beginner
2761 
2762 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2763 @*/
2764 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2765 {
2766   DM_Plex *mesh = (DM_Plex *)dm->data;
2767 
2768   PetscFunctionBegin;
2769   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2770   PetscValidIntPointer(size, 3);
2771   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2772   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2773   PetscFunctionReturn(0);
2774 }
2775 
2776 /*@
2777   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2778 
2779   Not Collective
2780 
2781   Input Parameters:
2782 + mesh - The `DMPLEX`
2783 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2784 - size - The cone size for point p
2785 
2786   Level: beginner
2787 
2788   Note:
2789   This should be called after `DMPlexSetChart()`.
2790 
2791 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2792 @*/
2793 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2794 {
2795   DM_Plex *mesh = (DM_Plex *)dm->data;
2796 
2797   PetscFunctionBegin;
2798   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2799   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2800   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2801   PetscFunctionReturn(0);
2802 }
2803 
2804 /*@C
2805   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2806 
2807   Not Collective
2808 
2809   Input Parameters:
2810 + dm - The `DMPLEX`
2811 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2812 
2813   Output Parameter:
2814 . cone - An array of points which are on the in-edges for point p
2815 
2816   Level: beginner
2817 
2818   Fortran Note:
2819   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2820   `DMPlexRestoreCone()` is not needed/available in C.
2821 
2822 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2823 @*/
2824 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2825 {
2826   DM_Plex *mesh = (DM_Plex *)dm->data;
2827   PetscInt off;
2828 
2829   PetscFunctionBegin;
2830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2831   PetscValidPointer(cone, 3);
2832   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2833   *cone = &mesh->cones[off];
2834   PetscFunctionReturn(0);
2835 }
2836 
2837 /*@C
2838   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2839 
2840   Not Collective
2841 
2842   Input Parameters:
2843 + dm - The `DMPLEX`
2844 - p - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2845 
2846   Output Parameters:
2847 + pConesSection - `PetscSection` describing the layout of pCones
2848 - pCones - An array of points which are on the in-edges for the point set p
2849 
2850   Level: intermediate
2851 
2852 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2853 @*/
2854 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2855 {
2856   PetscSection cs, newcs;
2857   PetscInt    *cones;
2858   PetscInt    *newarr = NULL;
2859   PetscInt     n;
2860 
2861   PetscFunctionBegin;
2862   PetscCall(DMPlexGetCones(dm, &cones));
2863   PetscCall(DMPlexGetConeSection(dm, &cs));
2864   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2865   if (pConesSection) *pConesSection = newcs;
2866   if (pCones) {
2867     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2868     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2869   }
2870   PetscFunctionReturn(0);
2871 }
2872 
2873 /*@
2874   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2875 
2876   Not Collective
2877 
2878   Input Parameters:
2879 + dm - The `DMPLEX`
2880 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2881 
2882   Output Parameter:
2883 . expandedPoints - An array of vertices recursively expanded from input points
2884 
2885   Level: advanced
2886 
2887   Notes:
2888   Like `DMPlexGetConeRecursive()` but returns only the 0-depth IS (i.e. vertices only) and no sections.
2889 
2890   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2891 
2892 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2893           `DMPlexGetDepth()`, `IS`
2894 @*/
2895 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2896 {
2897   IS      *expandedPointsAll;
2898   PetscInt depth;
2899 
2900   PetscFunctionBegin;
2901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2902   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2903   PetscValidPointer(expandedPoints, 3);
2904   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2905   *expandedPoints = expandedPointsAll[0];
2906   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2907   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2908   PetscFunctionReturn(0);
2909 }
2910 
2911 /*@
2912   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
2913 
2914   Not Collective
2915 
2916   Input Parameters:
2917 + dm - The `DMPLEX`
2918 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2919 
2920   Output Parameters:
2921 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2922 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2923 - sections - (optional) An array of sections which describe mappings from points to their cone points
2924 
2925   Level: advanced
2926 
2927   Notes:
2928   Like `DMPlexGetConeTuple()` but recursive.
2929 
2930   Array expandedPoints has size equal to depth. Each expandedPoints[d] contains DAG points with maximum depth d, recursively cone-wise expanded from the input points.
2931   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2932 
2933   Array section has size equal to depth.  Each `PetscSection` sections[d] realizes mapping from expandedPoints[d+1] (section points) to expandedPoints[d] (section dofs) as follows:
2934   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2935   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2936 
2937 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2938           `DMPlexGetDepth()`, `PetscSection`, `IS`
2939 @*/
2940 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2941 {
2942   const PetscInt *arr0 = NULL, *cone = NULL;
2943   PetscInt       *arr = NULL, *newarr = NULL;
2944   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2945   IS             *expandedPoints_;
2946   PetscSection   *sections_;
2947 
2948   PetscFunctionBegin;
2949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2950   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2951   if (depth) PetscValidIntPointer(depth, 3);
2952   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2953   if (sections) PetscValidPointer(sections, 5);
2954   PetscCall(ISGetLocalSize(points, &n));
2955   PetscCall(ISGetIndices(points, &arr0));
2956   PetscCall(DMPlexGetDepth(dm, &depth_));
2957   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2958   PetscCall(PetscCalloc1(depth_, &sections_));
2959   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2960   for (d = depth_ - 1; d >= 0; d--) {
2961     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2962     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2963     for (i = 0; i < n; i++) {
2964       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2965       if (arr[i] >= start && arr[i] < end) {
2966         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2967         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2968       } else {
2969         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2970       }
2971     }
2972     PetscCall(PetscSectionSetUp(sections_[d]));
2973     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2974     PetscCall(PetscMalloc1(newn, &newarr));
2975     for (i = 0; i < n; i++) {
2976       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2977       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2978       if (cn > 1) {
2979         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2980         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2981       } else {
2982         newarr[co] = arr[i];
2983       }
2984     }
2985     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2986     arr = newarr;
2987     n   = newn;
2988   }
2989   PetscCall(ISRestoreIndices(points, &arr0));
2990   *depth = depth_;
2991   if (expandedPoints) *expandedPoints = expandedPoints_;
2992   else {
2993     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2994     PetscCall(PetscFree(expandedPoints_));
2995   }
2996   if (sections) *sections = sections_;
2997   else {
2998     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2999     PetscCall(PetscFree(sections_));
3000   }
3001   PetscFunctionReturn(0);
3002 }
3003 
3004 /*@
3005   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3006 
3007   Not Collective
3008 
3009   Input Parameters:
3010 + dm - The `DMPLEX`
3011 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3012 
3013   Output Parameters:
3014 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3015 . expandedPoints - (optional) An array of recursively expanded cones
3016 - sections - (optional) An array of sections which describe mappings from points to their cone points
3017 
3018   Level: advanced
3019 
3020   Note:
3021   See `DMPlexGetConeRecursive()`
3022 
3023 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3024           `DMPlexGetDepth()`, `IS`, `PetscSection`
3025 @*/
3026 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3027 {
3028   PetscInt d, depth_;
3029 
3030   PetscFunctionBegin;
3031   PetscCall(DMPlexGetDepth(dm, &depth_));
3032   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3033   if (depth) *depth = 0;
3034   if (expandedPoints) {
3035     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3036     PetscCall(PetscFree(*expandedPoints));
3037   }
3038   if (sections) {
3039     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3040     PetscCall(PetscFree(*sections));
3041   }
3042   PetscFunctionReturn(0);
3043 }
3044 
3045 /*@
3046   DMPlexSetCone - Set the points on the in-edges for this point in the DAG; that is these are the points that cover the specific point
3047 
3048   Not Collective
3049 
3050   Input Parameters:
3051 + mesh - The `DMPLEX`
3052 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3053 - cone - An array of points which are on the in-edges for point p
3054 
3055   Level: beginner
3056 
3057   Note:
3058   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3059 
3060 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3061 @*/
3062 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3063 {
3064   DM_Plex *mesh = (DM_Plex *)dm->data;
3065   PetscInt pStart, pEnd;
3066   PetscInt dof, off, c;
3067 
3068   PetscFunctionBegin;
3069   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3070   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3071   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3072   if (dof) PetscValidIntPointer(cone, 3);
3073   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3074   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3075   for (c = 0; c < dof; ++c) {
3076     PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
3077     mesh->cones[off + c] = cone[c];
3078   }
3079   PetscFunctionReturn(0);
3080 }
3081 
3082 /*@C
3083   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3084 
3085   Not Collective
3086 
3087   Input Parameters:
3088 + mesh - The `DMPLEX`
3089 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3090 
3091   Output Parameter:
3092 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3093                     integer giving the prescription for cone traversal.
3094 
3095   Level: beginner
3096 
3097   Note:
3098   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3099   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3100   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3101   with the identity.
3102 
3103   Fortran Note:
3104   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3105   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3106 
3107 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3108 @*/
3109 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3110 {
3111   DM_Plex *mesh = (DM_Plex *)dm->data;
3112   PetscInt off;
3113 
3114   PetscFunctionBegin;
3115   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3116   if (PetscDefined(USE_DEBUG)) {
3117     PetscInt dof;
3118     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3119     if (dof) PetscValidPointer(coneOrientation, 3);
3120   }
3121   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3122 
3123   *coneOrientation = &mesh->coneOrientations[off];
3124   PetscFunctionReturn(0);
3125 }
3126 
3127 /*@
3128   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3129 
3130   Not Collective
3131 
3132   Input Parameters:
3133 + mesh - The `DMPLEX`
3134 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3135 - coneOrientation - An array of orientations
3136 
3137   Level: beginner
3138 
3139   Notes:
3140   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3141 
3142   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3143 
3144 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3145 @*/
3146 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3147 {
3148   DM_Plex *mesh = (DM_Plex *)dm->data;
3149   PetscInt pStart, pEnd;
3150   PetscInt dof, off, c;
3151 
3152   PetscFunctionBegin;
3153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3154   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3155   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3156   if (dof) PetscValidIntPointer(coneOrientation, 3);
3157   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3158   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3159   for (c = 0; c < dof; ++c) {
3160     PetscInt cdof, o = coneOrientation[c];
3161 
3162     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3163     PetscCheck(!o || (o >= -(cdof + 1) && o < cdof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof + 1), cdof);
3164     mesh->coneOrientations[off + c] = o;
3165   }
3166   PetscFunctionReturn(0);
3167 }
3168 
3169 /*@
3170   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3171 
3172   Not Collective
3173 
3174   Input Parameters:
3175 + mesh - The `DMPLEX`
3176 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3177 . conePos - The local index in the cone where the point should be put
3178 - conePoint - The mesh point to insert
3179 
3180   Level: beginner
3181 
3182 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3183 @*/
3184 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3185 {
3186   DM_Plex *mesh = (DM_Plex *)dm->data;
3187   PetscInt pStart, pEnd;
3188   PetscInt dof, off;
3189 
3190   PetscFunctionBegin;
3191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3192   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3193   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3194   PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3195   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3196   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3197   PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3198   mesh->cones[off + conePos] = conePoint;
3199   PetscFunctionReturn(0);
3200 }
3201 
3202 /*@
3203   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3204 
3205   Not Collective
3206 
3207   Input Parameters:
3208 + mesh - The `DMPLEX`
3209 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3210 . conePos - The local index in the cone where the point should be put
3211 - coneOrientation - The point orientation to insert
3212 
3213   Level: beginner
3214 
3215   Note:
3216   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3217 
3218 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3219 @*/
3220 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3221 {
3222   DM_Plex *mesh = (DM_Plex *)dm->data;
3223   PetscInt pStart, pEnd;
3224   PetscInt dof, off;
3225 
3226   PetscFunctionBegin;
3227   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3228   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3229   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3230   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3231   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3232   PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3233   mesh->coneOrientations[off + conePos] = coneOrientation;
3234   PetscFunctionReturn(0);
3235 }
3236 
3237 /*@C
3238   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3239 
3240   Not collective
3241 
3242   Input Parameters:
3243 + dm - The DMPlex
3244 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3245 
3246   Output Parameters:
3247 + cone - An array of points which are on the in-edges for point p
3248 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3249         integer giving the prescription for cone traversal.
3250 
3251   Level: beginner
3252 
3253   Notes:
3254   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3255   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3256   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3257   with the identity.
3258 
3259   Fortran Notes:
3260   Since it returns an array, this routine is only available in Fortran 90, and you must
3261   include petsc.h90 in your code.
3262   You must also call DMPlexRestoreCone() after you finish using the returned array.
3263   DMPlexRestoreCone() is not needed/available in C.
3264 
3265 .seealso: `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3266 @*/
3267 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3268 {
3269   DM_Plex *mesh = (DM_Plex *)dm->data;
3270 
3271   PetscFunctionBegin;
3272   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3273   if (mesh->tr) {
3274     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3275   } else {
3276     PetscInt off;
3277     if (PetscDefined(USE_DEBUG)) {
3278       PetscInt dof;
3279       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3280       if (dof) {
3281         if (cone) PetscValidPointer(cone, 3);
3282         if (ornt) PetscValidPointer(ornt, 4);
3283       }
3284     }
3285     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3286     if (cone) *cone = &mesh->cones[off];
3287     if (ornt) *ornt = &mesh->coneOrientations[off];
3288   }
3289   PetscFunctionReturn(0);
3290 }
3291 
3292 /*@C
3293   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3294 
3295   Not collective
3296 
3297   Input Parameters:
3298 + dm - The DMPlex
3299 . p  - The point, which must lie in the chart set with DMPlexSetChart()
3300 . cone - An array of points which are on the in-edges for point p
3301 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3302         integer giving the prescription for cone traversal.
3303 
3304   Level: beginner
3305 
3306   Notes:
3307   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3308   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3309   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3310   with the identity.
3311 
3312   Fortran Notes:
3313   Since it returns an array, this routine is only available in Fortran 90, and you must
3314   include petsc.h90 in your code.
3315   You must also call DMPlexRestoreCone() after you finish using the returned array.
3316   DMPlexRestoreCone() is not needed/available in C.
3317 
3318 .seealso: `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3319 @*/
3320 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3321 {
3322   DM_Plex *mesh = (DM_Plex *)dm->data;
3323 
3324   PetscFunctionBegin;
3325   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3326   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3327   PetscFunctionReturn(0);
3328 }
3329 
3330 /*@
3331   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3332 
3333   Not Collective
3334 
3335   Input Parameters:
3336 + mesh - The `DMPLEX`
3337 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3338 
3339   Output Parameter:
3340 . size - The support size for point p
3341 
3342   Level: beginner
3343 
3344 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3345 @*/
3346 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3347 {
3348   DM_Plex *mesh = (DM_Plex *)dm->data;
3349 
3350   PetscFunctionBegin;
3351   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3352   PetscValidIntPointer(size, 3);
3353   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3354   PetscFunctionReturn(0);
3355 }
3356 
3357 /*@
3358   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3359 
3360   Not Collective
3361 
3362   Input Parameters:
3363 + mesh - The `DMPLEX`
3364 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3365 - size - The support size for point p
3366 
3367   Level: beginner
3368 
3369   Note:
3370   This should be called after DMPlexSetChart().
3371 
3372 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3373 @*/
3374 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3375 {
3376   DM_Plex *mesh = (DM_Plex *)dm->data;
3377 
3378   PetscFunctionBegin;
3379   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3380   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3381   PetscFunctionReturn(0);
3382 }
3383 
3384 /*@C
3385   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3386 
3387   Not Collective
3388 
3389   Input Parameters:
3390 + mesh - The `DMPLEX`
3391 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3392 
3393   Output Parameter:
3394 . support - An array of points which are on the out-edges for point p
3395 
3396   Level: beginner
3397 
3398   Fortran Note:
3399   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3400   `DMPlexRestoreSupport()` is not needed/available in C.
3401 
3402 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3403 @*/
3404 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3405 {
3406   DM_Plex *mesh = (DM_Plex *)dm->data;
3407   PetscInt off;
3408 
3409   PetscFunctionBegin;
3410   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3411   PetscValidPointer(support, 3);
3412   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3413   *support = &mesh->supports[off];
3414   PetscFunctionReturn(0);
3415 }
3416 
3417 /*@
3418   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3419 
3420   Not Collective
3421 
3422   Input Parameters:
3423 + mesh - The `DMPLEX`
3424 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3425 - support - An array of points which are on the out-edges for point p
3426 
3427   Level: beginner
3428 
3429   Note:
3430   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3431 
3432 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3433 @*/
3434 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3435 {
3436   DM_Plex *mesh = (DM_Plex *)dm->data;
3437   PetscInt pStart, pEnd;
3438   PetscInt dof, off, c;
3439 
3440   PetscFunctionBegin;
3441   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3442   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3443   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3444   if (dof) PetscValidIntPointer(support, 3);
3445   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3446   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3447   for (c = 0; c < dof; ++c) {
3448     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3449     mesh->supports[off + c] = support[c];
3450   }
3451   PetscFunctionReturn(0);
3452 }
3453 
3454 /*@
3455   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3456 
3457   Not Collective
3458 
3459   Input Parameters:
3460 + mesh - The `DMPLEX`
3461 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3462 . supportPos - The local index in the cone where the point should be put
3463 - supportPoint - The mesh point to insert
3464 
3465   Level: beginner
3466 
3467 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3468 @*/
3469 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3470 {
3471   DM_Plex *mesh = (DM_Plex *)dm->data;
3472   PetscInt pStart, pEnd;
3473   PetscInt dof, off;
3474 
3475   PetscFunctionBegin;
3476   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3477   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3478   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3479   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3480   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3481   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3482   PetscCheck(supportPos < dof, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3483   mesh->supports[off + supportPos] = supportPoint;
3484   PetscFunctionReturn(0);
3485 }
3486 
3487 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3488 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3489 {
3490   switch (ct) {
3491   case DM_POLYTOPE_SEGMENT:
3492     if (o == -1) return -2;
3493     break;
3494   case DM_POLYTOPE_TRIANGLE:
3495     if (o == -3) return -1;
3496     if (o == -2) return -3;
3497     if (o == -1) return -2;
3498     break;
3499   case DM_POLYTOPE_QUADRILATERAL:
3500     if (o == -4) return -2;
3501     if (o == -3) return -1;
3502     if (o == -2) return -4;
3503     if (o == -1) return -3;
3504     break;
3505   default:
3506     return o;
3507   }
3508   return o;
3509 }
3510 
3511 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3512 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3513 {
3514   switch (ct) {
3515   case DM_POLYTOPE_SEGMENT:
3516     if ((o == -2) || (o == 1)) return -1;
3517     if (o == -1) return 0;
3518     break;
3519   case DM_POLYTOPE_TRIANGLE:
3520     if (o == -3) return -2;
3521     if (o == -2) return -1;
3522     if (o == -1) return -3;
3523     break;
3524   case DM_POLYTOPE_QUADRILATERAL:
3525     if (o == -4) return -2;
3526     if (o == -3) return -1;
3527     if (o == -2) return -4;
3528     if (o == -1) return -3;
3529     break;
3530   default:
3531     return o;
3532   }
3533   return o;
3534 }
3535 
3536 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3537 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3538 {
3539   PetscInt pStart, pEnd, p;
3540 
3541   PetscFunctionBegin;
3542   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3543   for (p = pStart; p < pEnd; ++p) {
3544     const PetscInt *cone, *ornt;
3545     PetscInt        coneSize, c;
3546 
3547     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3548     PetscCall(DMPlexGetCone(dm, p, &cone));
3549     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3550     for (c = 0; c < coneSize; ++c) {
3551       DMPolytopeType ct;
3552       const PetscInt o = ornt[c];
3553 
3554       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3555       switch (ct) {
3556       case DM_POLYTOPE_SEGMENT:
3557         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3558         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3559         break;
3560       case DM_POLYTOPE_TRIANGLE:
3561         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3562         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3563         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3564         break;
3565       case DM_POLYTOPE_QUADRILATERAL:
3566         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3567         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3568         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3569         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3570         break;
3571       default:
3572         break;
3573       }
3574     }
3575   }
3576   PetscFunctionReturn(0);
3577 }
3578 
3579 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3580 {
3581   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3582   PetscInt       *closure;
3583   const PetscInt *tmp = NULL, *tmpO = NULL;
3584   PetscInt        off = 0, tmpSize, t;
3585 
3586   PetscFunctionBeginHot;
3587   if (ornt) {
3588     PetscCall(DMPlexGetCellType(dm, p, &ct));
3589     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3590   }
3591   if (*points) {
3592     closure = *points;
3593   } else {
3594     PetscInt maxConeSize, maxSupportSize;
3595     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3596     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3597   }
3598   if (useCone) {
3599     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3600     PetscCall(DMPlexGetCone(dm, p, &tmp));
3601     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3602   } else {
3603     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3604     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3605   }
3606   if (ct == DM_POLYTOPE_UNKNOWN) {
3607     closure[off++] = p;
3608     closure[off++] = 0;
3609     for (t = 0; t < tmpSize; ++t) {
3610       closure[off++] = tmp[t];
3611       closure[off++] = tmpO ? tmpO[t] : 0;
3612     }
3613   } else {
3614     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3615 
3616     /* We assume that cells with a valid type have faces with a valid type */
3617     closure[off++] = p;
3618     closure[off++] = ornt;
3619     for (t = 0; t < tmpSize; ++t) {
3620       DMPolytopeType ft;
3621 
3622       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3623       closure[off++] = tmp[arr[t]];
3624       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3625     }
3626   }
3627   if (numPoints) *numPoints = tmpSize + 1;
3628   if (points) *points = closure;
3629   PetscFunctionReturn(0);
3630 }
3631 
3632 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3633 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3634 {
3635   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3636   const PetscInt *cone, *ornt;
3637   PetscInt       *pts, *closure = NULL;
3638   DMPolytopeType  ft;
3639   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3640   PetscInt        dim, coneSize, c, d, clSize, cl;
3641 
3642   PetscFunctionBeginHot;
3643   PetscCall(DMGetDimension(dm, &dim));
3644   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3645   PetscCall(DMPlexGetCone(dm, point, &cone));
3646   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3647   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3648   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3649   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3650   maxSize       = PetscMax(coneSeries, supportSeries);
3651   if (*points) {
3652     pts = *points;
3653   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3654   c        = 0;
3655   pts[c++] = point;
3656   pts[c++] = o;
3657   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3658   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3659   for (cl = 0; cl < clSize * 2; cl += 2) {
3660     pts[c++] = closure[cl];
3661     pts[c++] = closure[cl + 1];
3662   }
3663   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3664   for (cl = 0; cl < clSize * 2; cl += 2) {
3665     pts[c++] = closure[cl];
3666     pts[c++] = closure[cl + 1];
3667   }
3668   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3669   for (d = 2; d < coneSize; ++d) {
3670     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3671     pts[c++] = cone[arr[d * 2 + 0]];
3672     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3673   }
3674   if (dim >= 3) {
3675     for (d = 2; d < coneSize; ++d) {
3676       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3677       const PetscInt *fcone, *fornt;
3678       PetscInt        fconeSize, fc, i;
3679 
3680       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3681       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3682       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3683       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3684       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3685       for (fc = 0; fc < fconeSize; ++fc) {
3686         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3687         const PetscInt co = farr[fc * 2 + 1];
3688 
3689         for (i = 0; i < c; i += 2)
3690           if (pts[i] == cp) break;
3691         if (i == c) {
3692           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3693           pts[c++] = cp;
3694           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3695         }
3696       }
3697     }
3698   }
3699   *numPoints = c / 2;
3700   *points    = pts;
3701   PetscFunctionReturn(0);
3702 }
3703 
3704 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3705 {
3706   DMPolytopeType ct;
3707   PetscInt      *closure, *fifo;
3708   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3709   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3710   PetscInt       depth, maxSize;
3711 
3712   PetscFunctionBeginHot;
3713   PetscCall(DMPlexGetDepth(dm, &depth));
3714   if (depth == 1) {
3715     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3716     PetscFunctionReturn(0);
3717   }
3718   PetscCall(DMPlexGetCellType(dm, p, &ct));
3719   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3720   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3721     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3722     PetscFunctionReturn(0);
3723   }
3724   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3725   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3726   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3727   maxSize       = PetscMax(coneSeries, supportSeries);
3728   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3729   if (*points) {
3730     closure = *points;
3731   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3732   closure[closureSize++] = p;
3733   closure[closureSize++] = ornt;
3734   fifo[fifoSize++]       = p;
3735   fifo[fifoSize++]       = ornt;
3736   fifo[fifoSize++]       = ct;
3737   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3738   while (fifoSize - fifoStart) {
3739     const PetscInt       q    = fifo[fifoStart++];
3740     const PetscInt       o    = fifo[fifoStart++];
3741     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3742     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3743     const PetscInt      *tmp, *tmpO;
3744     PetscInt             tmpSize, t;
3745 
3746     if (PetscDefined(USE_DEBUG)) {
3747       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3748       PetscCheck(!o || !(o >= nO || o < -nO), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
3749     }
3750     if (useCone) {
3751       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3752       PetscCall(DMPlexGetCone(dm, q, &tmp));
3753       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3754     } else {
3755       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3756       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3757       tmpO = NULL;
3758     }
3759     for (t = 0; t < tmpSize; ++t) {
3760       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3761       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3762       const PetscInt cp = tmp[ip];
3763       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3764       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3765       PetscInt       c;
3766 
3767       /* Check for duplicate */
3768       for (c = 0; c < closureSize; c += 2) {
3769         if (closure[c] == cp) break;
3770       }
3771       if (c == closureSize) {
3772         closure[closureSize++] = cp;
3773         closure[closureSize++] = co;
3774         fifo[fifoSize++]       = cp;
3775         fifo[fifoSize++]       = co;
3776         fifo[fifoSize++]       = ct;
3777       }
3778     }
3779   }
3780   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3781   if (numPoints) *numPoints = closureSize / 2;
3782   if (points) *points = closure;
3783   PetscFunctionReturn(0);
3784 }
3785 
3786 /*@C
3787   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3788 
3789   Not Collective
3790 
3791   Input Parameters:
3792 + dm      - The `DMPLEX`
3793 . p       - The mesh point
3794 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3795 
3796   Input/Output Parameter:
3797 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3798            if NULL on input, internal storage will be returned, otherwise the provided array is used
3799 
3800   Output Parameter:
3801 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3802 
3803   Level: beginner
3804 
3805   Note:
3806   If using internal storage (points is NULL on input), each call overwrites the last output.
3807 
3808   Fortran Note:
3809   The numPoints argument is not present in the Fortran binding since it is internal to the array.
3810 
3811 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3812 @*/
3813 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3814 {
3815   PetscFunctionBeginHot;
3816   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3817   if (numPoints) PetscValidIntPointer(numPoints, 4);
3818   if (points) PetscValidPointer(points, 5);
3819   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3820   PetscFunctionReturn(0);
3821 }
3822 
3823 /*@C
3824   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3825 
3826   Not Collective
3827 
3828   Input Parameters:
3829 + dm        - The `DMPLEX`
3830 . p         - The mesh point
3831 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3832 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3833 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3834 
3835   Level: beginner
3836 
3837   Note:
3838   If not using internal storage (points is not NULL on input), this call is unnecessary
3839 
3840 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3841 @*/
3842 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3843 {
3844   PetscFunctionBeginHot;
3845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3846   if (numPoints) *numPoints = 0;
3847   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3848   PetscFunctionReturn(0);
3849 }
3850 
3851 /*@
3852   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3853 
3854   Not Collective
3855 
3856   Input Parameter:
3857 . mesh - The `DMPLEX`
3858 
3859   Output Parameters:
3860 + maxConeSize - The maximum number of in-edges
3861 - maxSupportSize - The maximum number of out-edges
3862 
3863   Level: beginner
3864 
3865 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3866 @*/
3867 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3868 {
3869   DM_Plex *mesh = (DM_Plex *)dm->data;
3870 
3871   PetscFunctionBegin;
3872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3873   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3874   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3875   PetscFunctionReturn(0);
3876 }
3877 
3878 PetscErrorCode DMSetUp_Plex(DM dm)
3879 {
3880   DM_Plex *mesh = (DM_Plex *)dm->data;
3881   PetscInt size, maxSupportSize;
3882 
3883   PetscFunctionBegin;
3884   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3885   PetscCall(PetscSectionSetUp(mesh->coneSection));
3886   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3887   PetscCall(PetscMalloc1(size, &mesh->cones));
3888   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3889   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3890   if (maxSupportSize) {
3891     PetscCall(PetscSectionSetUp(mesh->supportSection));
3892     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3893     PetscCall(PetscMalloc1(size, &mesh->supports));
3894   }
3895   PetscFunctionReturn(0);
3896 }
3897 
3898 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3899 {
3900   PetscFunctionBegin;
3901   if (subdm) PetscCall(DMClone(dm, subdm));
3902   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3903   if (subdm) (*subdm)->useNatural = dm->useNatural;
3904   if (dm->useNatural && dm->sfMigration) {
3905     PetscSF sfNatural;
3906 
3907     (*subdm)->sfMigration = dm->sfMigration;
3908     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3909     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3910     (*subdm)->sfNatural = sfNatural;
3911   }
3912   PetscFunctionReturn(0);
3913 }
3914 
3915 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3916 {
3917   PetscInt i = 0;
3918 
3919   PetscFunctionBegin;
3920   PetscCall(DMClone(dms[0], superdm));
3921   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3922   (*superdm)->useNatural = PETSC_FALSE;
3923   for (i = 0; i < len; i++) {
3924     if (dms[i]->useNatural && dms[i]->sfMigration) {
3925       PetscSF sfNatural;
3926 
3927       (*superdm)->sfMigration = dms[i]->sfMigration;
3928       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3929       (*superdm)->useNatural = PETSC_TRUE;
3930       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3931       (*superdm)->sfNatural = sfNatural;
3932       break;
3933     }
3934   }
3935   PetscFunctionReturn(0);
3936 }
3937 
3938 /*@
3939   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3940 
3941   Not Collective
3942 
3943   Input Parameter:
3944 . mesh - The `DMPLEX`
3945 
3946   Level: beginner
3947 
3948   Note:
3949   This should be called after all calls to `DMPlexSetCone()`
3950 
3951 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3952 @*/
3953 PetscErrorCode DMPlexSymmetrize(DM dm)
3954 {
3955   DM_Plex  *mesh = (DM_Plex *)dm->data;
3956   PetscInt *offsets;
3957   PetscInt  supportSize;
3958   PetscInt  pStart, pEnd, p;
3959 
3960   PetscFunctionBegin;
3961   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3962   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3963   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3964   /* Calculate support sizes */
3965   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3966   for (p = pStart; p < pEnd; ++p) {
3967     PetscInt dof, off, c;
3968 
3969     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3970     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3971     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3972   }
3973   PetscCall(PetscSectionSetUp(mesh->supportSection));
3974   /* Calculate supports */
3975   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3976   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3977   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3978   for (p = pStart; p < pEnd; ++p) {
3979     PetscInt dof, off, c;
3980 
3981     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3982     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3983     for (c = off; c < off + dof; ++c) {
3984       const PetscInt q = mesh->cones[c];
3985       PetscInt       offS;
3986 
3987       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3988 
3989       mesh->supports[offS + offsets[q]] = p;
3990       ++offsets[q];
3991     }
3992   }
3993   PetscCall(PetscFree(offsets));
3994   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
3995   PetscFunctionReturn(0);
3996 }
3997 
3998 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3999 {
4000   IS stratumIS;
4001 
4002   PetscFunctionBegin;
4003   if (pStart >= pEnd) PetscFunctionReturn(0);
4004   if (PetscDefined(USE_DEBUG)) {
4005     PetscInt  qStart, qEnd, numLevels, level;
4006     PetscBool overlap = PETSC_FALSE;
4007     PetscCall(DMLabelGetNumValues(label, &numLevels));
4008     for (level = 0; level < numLevels; level++) {
4009       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4010       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4011         overlap = PETSC_TRUE;
4012         break;
4013       }
4014     }
4015     PetscCheck(!overlap, PETSC_COMM_SELF, PETSC_ERR_PLIB, "New depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ") overlaps with depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ")", depth, pStart, pEnd, level, qStart, qEnd);
4016   }
4017   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4018   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4019   PetscCall(ISDestroy(&stratumIS));
4020   PetscFunctionReturn(0);
4021 }
4022 
4023 /*@
4024   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4025   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4026   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4027   the DAG.
4028 
4029   Collective on dm
4030 
4031   Input Parameter:
4032 . mesh - The `DMPLEX`
4033 
4034   Level: beginner
4035 
4036   Notes:
4037   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4038   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4039   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4040   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4041   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4042 
4043   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4044   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4045   we had a mesh consisting of one triangle (c0) and three vertices (v0, v1, v2), and only one edge is on the boundary so we choose
4046   to interpolate only that one (e0), so that
4047 .vb
4048   cone(c0) = {e0, v2}
4049   cone(e0) = {v0, v1}
4050 .ve
4051   If `DMPlexStratify()` is run on this mesh, it will give depths
4052 .vb
4053    depth 0 = {v0, v1, v2}
4054    depth 1 = {e0, c0}
4055 .ve
4056   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4057 
4058   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4059 
4060 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4061 @*/
4062 PetscErrorCode DMPlexStratify(DM dm)
4063 {
4064   DM_Plex *mesh = (DM_Plex *)dm->data;
4065   DMLabel  label;
4066   PetscInt pStart, pEnd, p;
4067   PetscInt numRoots = 0, numLeaves = 0;
4068 
4069   PetscFunctionBegin;
4070   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4071   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4072 
4073   /* Create depth label */
4074   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4075   PetscCall(DMCreateLabel(dm, "depth"));
4076   PetscCall(DMPlexGetDepthLabel(dm, &label));
4077 
4078   {
4079     /* Initialize roots and count leaves */
4080     PetscInt sMin = PETSC_MAX_INT;
4081     PetscInt sMax = PETSC_MIN_INT;
4082     PetscInt coneSize, supportSize;
4083 
4084     for (p = pStart; p < pEnd; ++p) {
4085       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4086       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4087       if (!coneSize && supportSize) {
4088         sMin = PetscMin(p, sMin);
4089         sMax = PetscMax(p, sMax);
4090         ++numRoots;
4091       } else if (!supportSize && coneSize) {
4092         ++numLeaves;
4093       } else if (!supportSize && !coneSize) {
4094         /* Isolated points */
4095         sMin = PetscMin(p, sMin);
4096         sMax = PetscMax(p, sMax);
4097       }
4098     }
4099     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4100   }
4101 
4102   if (numRoots + numLeaves == (pEnd - pStart)) {
4103     PetscInt sMin = PETSC_MAX_INT;
4104     PetscInt sMax = PETSC_MIN_INT;
4105     PetscInt coneSize, supportSize;
4106 
4107     for (p = pStart; p < pEnd; ++p) {
4108       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4109       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4110       if (!supportSize && coneSize) {
4111         sMin = PetscMin(p, sMin);
4112         sMax = PetscMax(p, sMax);
4113       }
4114     }
4115     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4116   } else {
4117     PetscInt level = 0;
4118     PetscInt qStart, qEnd, q;
4119 
4120     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4121     while (qEnd > qStart) {
4122       PetscInt sMin = PETSC_MAX_INT;
4123       PetscInt sMax = PETSC_MIN_INT;
4124 
4125       for (q = qStart; q < qEnd; ++q) {
4126         const PetscInt *support;
4127         PetscInt        supportSize, s;
4128 
4129         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4130         PetscCall(DMPlexGetSupport(dm, q, &support));
4131         for (s = 0; s < supportSize; ++s) {
4132           sMin = PetscMin(support[s], sMin);
4133           sMax = PetscMax(support[s], sMax);
4134         }
4135       }
4136       PetscCall(DMLabelGetNumValues(label, &level));
4137       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4138       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4139     }
4140   }
4141   { /* just in case there is an empty process */
4142     PetscInt numValues, maxValues = 0, v;
4143 
4144     PetscCall(DMLabelGetNumValues(label, &numValues));
4145     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4146     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4147   }
4148   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4149   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4150   PetscFunctionReturn(0);
4151 }
4152 
4153 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4154 {
4155   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4156   PetscInt       dim, depth, pheight, coneSize;
4157 
4158   PetscFunctionBeginHot;
4159   PetscCall(DMGetDimension(dm, &dim));
4160   PetscCall(DMPlexGetDepth(dm, &depth));
4161   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4162   pheight = depth - pdepth;
4163   if (depth <= 1) {
4164     switch (pdepth) {
4165     case 0:
4166       ct = DM_POLYTOPE_POINT;
4167       break;
4168     case 1:
4169       switch (coneSize) {
4170       case 2:
4171         ct = DM_POLYTOPE_SEGMENT;
4172         break;
4173       case 3:
4174         ct = DM_POLYTOPE_TRIANGLE;
4175         break;
4176       case 4:
4177         switch (dim) {
4178         case 2:
4179           ct = DM_POLYTOPE_QUADRILATERAL;
4180           break;
4181         case 3:
4182           ct = DM_POLYTOPE_TETRAHEDRON;
4183           break;
4184         default:
4185           break;
4186         }
4187         break;
4188       case 5:
4189         ct = DM_POLYTOPE_PYRAMID;
4190         break;
4191       case 6:
4192         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4193         break;
4194       case 8:
4195         ct = DM_POLYTOPE_HEXAHEDRON;
4196         break;
4197       default:
4198         break;
4199       }
4200     }
4201   } else {
4202     if (pdepth == 0) {
4203       ct = DM_POLYTOPE_POINT;
4204     } else if (pheight == 0) {
4205       switch (dim) {
4206       case 1:
4207         switch (coneSize) {
4208         case 2:
4209           ct = DM_POLYTOPE_SEGMENT;
4210           break;
4211         default:
4212           break;
4213         }
4214         break;
4215       case 2:
4216         switch (coneSize) {
4217         case 3:
4218           ct = DM_POLYTOPE_TRIANGLE;
4219           break;
4220         case 4:
4221           ct = DM_POLYTOPE_QUADRILATERAL;
4222           break;
4223         default:
4224           break;
4225         }
4226         break;
4227       case 3:
4228         switch (coneSize) {
4229         case 4:
4230           ct = DM_POLYTOPE_TETRAHEDRON;
4231           break;
4232         case 5: {
4233           const PetscInt *cone;
4234           PetscInt        faceConeSize;
4235 
4236           PetscCall(DMPlexGetCone(dm, p, &cone));
4237           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4238           switch (faceConeSize) {
4239           case 3:
4240             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4241             break;
4242           case 4:
4243             ct = DM_POLYTOPE_PYRAMID;
4244             break;
4245           }
4246         } break;
4247         case 6:
4248           ct = DM_POLYTOPE_HEXAHEDRON;
4249           break;
4250         default:
4251           break;
4252         }
4253         break;
4254       default:
4255         break;
4256       }
4257     } else if (pheight > 0) {
4258       switch (coneSize) {
4259       case 2:
4260         ct = DM_POLYTOPE_SEGMENT;
4261         break;
4262       case 3:
4263         ct = DM_POLYTOPE_TRIANGLE;
4264         break;
4265       case 4:
4266         ct = DM_POLYTOPE_QUADRILATERAL;
4267         break;
4268       default:
4269         break;
4270       }
4271     }
4272   }
4273   *pt = ct;
4274   PetscFunctionReturn(0);
4275 }
4276 
4277 /*@
4278   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4279 
4280   Collective on dm
4281 
4282   Input Parameter:
4283 . mesh - The `DMPLEX`
4284 
4285   Level: developer
4286 
4287   Note:
4288   This function is normally called automatically when a cell type is requested. It creates an
4289   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4290   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4291 
4292   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4293 
4294 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4295 @*/
4296 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4297 {
4298   DM_Plex *mesh;
4299   DMLabel  ctLabel;
4300   PetscInt pStart, pEnd, p;
4301 
4302   PetscFunctionBegin;
4303   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4304   mesh = (DM_Plex *)dm->data;
4305   PetscCall(DMCreateLabel(dm, "celltype"));
4306   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4307   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4308   for (p = pStart; p < pEnd; ++p) {
4309     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4310     PetscInt       pdepth;
4311 
4312     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4313     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4314     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4315     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4316   }
4317   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4318   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4319   PetscFunctionReturn(0);
4320 }
4321 
4322 /*@C
4323   DMPlexGetJoin - Get an array for the join of the set of points
4324 
4325   Not Collective
4326 
4327   Input Parameters:
4328 + dm - The `DMPLEX` object
4329 . numPoints - The number of input points for the join
4330 - points - The input points
4331 
4332   Output Parameters:
4333 + numCoveredPoints - The number of points in the join
4334 - coveredPoints - The points in the join
4335 
4336   Level: intermediate
4337 
4338   Note:
4339   Currently, this is restricted to a single level join
4340 
4341   Fortran Note:
4342   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4343 
4344 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4345 @*/
4346 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4347 {
4348   DM_Plex  *mesh = (DM_Plex *)dm->data;
4349   PetscInt *join[2];
4350   PetscInt  joinSize, i = 0;
4351   PetscInt  dof, off, p, c, m;
4352   PetscInt  maxSupportSize;
4353 
4354   PetscFunctionBegin;
4355   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4356   PetscValidIntPointer(points, 3);
4357   PetscValidIntPointer(numCoveredPoints, 4);
4358   PetscValidPointer(coveredPoints, 5);
4359   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4360   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4361   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4362   /* Copy in support of first point */
4363   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4364   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4365   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4366   /* Check each successive support */
4367   for (p = 1; p < numPoints; ++p) {
4368     PetscInt newJoinSize = 0;
4369 
4370     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4371     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4372     for (c = 0; c < dof; ++c) {
4373       const PetscInt point = mesh->supports[off + c];
4374 
4375       for (m = 0; m < joinSize; ++m) {
4376         if (point == join[i][m]) {
4377           join[1 - i][newJoinSize++] = point;
4378           break;
4379         }
4380       }
4381     }
4382     joinSize = newJoinSize;
4383     i        = 1 - i;
4384   }
4385   *numCoveredPoints = joinSize;
4386   *coveredPoints    = join[i];
4387   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4388   PetscFunctionReturn(0);
4389 }
4390 
4391 /*@C
4392   DMPlexRestoreJoin - Restore an array for the join of the set of points
4393 
4394   Not Collective
4395 
4396   Input Parameters:
4397 + dm - The `DMPLEX` object
4398 . numPoints - The number of input points for the join
4399 - points - The input points
4400 
4401   Output Parameters:
4402 + numCoveredPoints - The number of points in the join
4403 - coveredPoints - The points in the join
4404 
4405   Level: intermediate
4406 
4407   Fortran Note:
4408   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4409 
4410 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4411 @*/
4412 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4413 {
4414   PetscFunctionBegin;
4415   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4416   if (points) PetscValidIntPointer(points, 3);
4417   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4418   PetscValidPointer(coveredPoints, 5);
4419   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4420   if (numCoveredPoints) *numCoveredPoints = 0;
4421   PetscFunctionReturn(0);
4422 }
4423 
4424 /*@C
4425   DMPlexGetFullJoin - Get an array for the join of the set of points
4426 
4427   Not Collective
4428 
4429   Input Parameters:
4430 + dm - The `DMPLEX` object
4431 . numPoints - The number of input points for the join
4432 - points - The input points
4433 
4434   Output Parameters:
4435 + numCoveredPoints - The number of points in the join
4436 - coveredPoints - The points in the join
4437 
4438   Level: intermediate
4439 
4440   Fortran Note:
4441   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4442 
4443 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4444 @*/
4445 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4446 {
4447   PetscInt *offsets, **closures;
4448   PetscInt *join[2];
4449   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4450   PetscInt  p, d, c, m, ms;
4451 
4452   PetscFunctionBegin;
4453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4454   PetscValidIntPointer(points, 3);
4455   PetscValidIntPointer(numCoveredPoints, 4);
4456   PetscValidPointer(coveredPoints, 5);
4457 
4458   PetscCall(DMPlexGetDepth(dm, &depth));
4459   PetscCall(PetscCalloc1(numPoints, &closures));
4460   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4461   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4462   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4463   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4464   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4465 
4466   for (p = 0; p < numPoints; ++p) {
4467     PetscInt closureSize;
4468 
4469     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4470 
4471     offsets[p * (depth + 2) + 0] = 0;
4472     for (d = 0; d < depth + 1; ++d) {
4473       PetscInt pStart, pEnd, i;
4474 
4475       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4476       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4477         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4478           offsets[p * (depth + 2) + d + 1] = i;
4479           break;
4480         }
4481       }
4482       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4483     }
4484     PetscCheck(offsets[p * (depth + 2) + depth + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (depth + 2) + depth + 1], closureSize);
4485   }
4486   for (d = 0; d < depth + 1; ++d) {
4487     PetscInt dof;
4488 
4489     /* Copy in support of first point */
4490     dof = offsets[d + 1] - offsets[d];
4491     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4492     /* Check each successive cone */
4493     for (p = 1; p < numPoints && joinSize; ++p) {
4494       PetscInt newJoinSize = 0;
4495 
4496       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4497       for (c = 0; c < dof; ++c) {
4498         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4499 
4500         for (m = 0; m < joinSize; ++m) {
4501           if (point == join[i][m]) {
4502             join[1 - i][newJoinSize++] = point;
4503             break;
4504           }
4505         }
4506       }
4507       joinSize = newJoinSize;
4508       i        = 1 - i;
4509     }
4510     if (joinSize) break;
4511   }
4512   *numCoveredPoints = joinSize;
4513   *coveredPoints    = join[i];
4514   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4515   PetscCall(PetscFree(closures));
4516   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4517   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4518   PetscFunctionReturn(0);
4519 }
4520 
4521 /*@C
4522   DMPlexGetMeet - Get an array for the meet of the set of points
4523 
4524   Not Collective
4525 
4526   Input Parameters:
4527 + dm - The `DMPLEX` object
4528 . numPoints - The number of input points for the meet
4529 - points - The input points
4530 
4531   Output Parameters:
4532 + numCoveredPoints - The number of points in the meet
4533 - coveredPoints - The points in the meet
4534 
4535   Level: intermediate
4536 
4537   Note:
4538   Currently, this is restricted to a single level meet
4539 
4540   Fortran Notes:
4541   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4542 
4543 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4544 @*/
4545 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4546 {
4547   DM_Plex  *mesh = (DM_Plex *)dm->data;
4548   PetscInt *meet[2];
4549   PetscInt  meetSize, i = 0;
4550   PetscInt  dof, off, p, c, m;
4551   PetscInt  maxConeSize;
4552 
4553   PetscFunctionBegin;
4554   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4555   PetscValidIntPointer(points, 3);
4556   PetscValidIntPointer(numCoveringPoints, 4);
4557   PetscValidPointer(coveringPoints, 5);
4558   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4559   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4560   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4561   /* Copy in cone of first point */
4562   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4563   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4564   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4565   /* Check each successive cone */
4566   for (p = 1; p < numPoints; ++p) {
4567     PetscInt newMeetSize = 0;
4568 
4569     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4570     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4571     for (c = 0; c < dof; ++c) {
4572       const PetscInt point = mesh->cones[off + c];
4573 
4574       for (m = 0; m < meetSize; ++m) {
4575         if (point == meet[i][m]) {
4576           meet[1 - i][newMeetSize++] = point;
4577           break;
4578         }
4579       }
4580     }
4581     meetSize = newMeetSize;
4582     i        = 1 - i;
4583   }
4584   *numCoveringPoints = meetSize;
4585   *coveringPoints    = meet[i];
4586   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4587   PetscFunctionReturn(0);
4588 }
4589 
4590 /*@C
4591   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4592 
4593   Not Collective
4594 
4595   Input Parameters:
4596 + dm - The `DMPLEX` object
4597 . numPoints - The number of input points for the meet
4598 - points - The input points
4599 
4600   Output Parameters:
4601 + numCoveredPoints - The number of points in the meet
4602 - coveredPoints - The points in the meet
4603 
4604   Level: intermediate
4605 
4606   Fortran Note:
4607   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4608 
4609 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4610 @*/
4611 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4612 {
4613   PetscFunctionBegin;
4614   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4615   if (points) PetscValidIntPointer(points, 3);
4616   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4617   PetscValidPointer(coveredPoints, 5);
4618   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4619   if (numCoveredPoints) *numCoveredPoints = 0;
4620   PetscFunctionReturn(0);
4621 }
4622 
4623 /*@C
4624   DMPlexGetFullMeet - Get an array for the meet of the set of points
4625 
4626   Not Collective
4627 
4628   Input Parameters:
4629 + dm - The `DMPLEX` object
4630 . numPoints - The number of input points for the meet
4631 - points - The input points
4632 
4633   Output Parameters:
4634 + numCoveredPoints - The number of points in the meet
4635 - coveredPoints - The points in the meet
4636 
4637   Level: intermediate
4638 
4639   Fortran Note:
4640   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4641 
4642 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4643 @*/
4644 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4645 {
4646   PetscInt *offsets, **closures;
4647   PetscInt *meet[2];
4648   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4649   PetscInt  p, h, c, m, mc;
4650 
4651   PetscFunctionBegin;
4652   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4653   PetscValidIntPointer(points, 3);
4654   PetscValidIntPointer(numCoveredPoints, 4);
4655   PetscValidPointer(coveredPoints, 5);
4656 
4657   PetscCall(DMPlexGetDepth(dm, &height));
4658   PetscCall(PetscMalloc1(numPoints, &closures));
4659   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4660   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4661   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4662   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4663   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4664 
4665   for (p = 0; p < numPoints; ++p) {
4666     PetscInt closureSize;
4667 
4668     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4669 
4670     offsets[p * (height + 2) + 0] = 0;
4671     for (h = 0; h < height + 1; ++h) {
4672       PetscInt pStart, pEnd, i;
4673 
4674       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4675       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4676         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4677           offsets[p * (height + 2) + h + 1] = i;
4678           break;
4679         }
4680       }
4681       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4682     }
4683     PetscCheck(offsets[p * (height + 2) + height + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (height + 2) + height + 1], closureSize);
4684   }
4685   for (h = 0; h < height + 1; ++h) {
4686     PetscInt dof;
4687 
4688     /* Copy in cone of first point */
4689     dof = offsets[h + 1] - offsets[h];
4690     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4691     /* Check each successive cone */
4692     for (p = 1; p < numPoints && meetSize; ++p) {
4693       PetscInt newMeetSize = 0;
4694 
4695       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4696       for (c = 0; c < dof; ++c) {
4697         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4698 
4699         for (m = 0; m < meetSize; ++m) {
4700           if (point == meet[i][m]) {
4701             meet[1 - i][newMeetSize++] = point;
4702             break;
4703           }
4704         }
4705       }
4706       meetSize = newMeetSize;
4707       i        = 1 - i;
4708     }
4709     if (meetSize) break;
4710   }
4711   *numCoveredPoints = meetSize;
4712   *coveredPoints    = meet[i];
4713   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4714   PetscCall(PetscFree(closures));
4715   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4716   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4717   PetscFunctionReturn(0);
4718 }
4719 
4720 /*@C
4721   DMPlexEqual - Determine if two `DM` have the same topology
4722 
4723   Not Collective
4724 
4725   Input Parameters:
4726 + dmA - A `DMPLEX` object
4727 - dmB - A `DMPLEX` object
4728 
4729   Output Parameters:
4730 . equal - `PETSC_TRUE` if the topologies are identical
4731 
4732   Level: intermediate
4733 
4734   Note:
4735   We are not solving graph isomorphism, so we do not permute.
4736 
4737 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4738 @*/
4739 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4740 {
4741   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4742 
4743   PetscFunctionBegin;
4744   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4745   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4746   PetscValidBoolPointer(equal, 3);
4747 
4748   *equal = PETSC_FALSE;
4749   PetscCall(DMPlexGetDepth(dmA, &depth));
4750   PetscCall(DMPlexGetDepth(dmB, &depthB));
4751   if (depth != depthB) PetscFunctionReturn(0);
4752   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4753   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4754   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4755   for (p = pStart; p < pEnd; ++p) {
4756     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4757     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4758 
4759     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4760     PetscCall(DMPlexGetCone(dmA, p, &cone));
4761     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4762     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4763     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4764     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4765     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4766     for (c = 0; c < coneSize; ++c) {
4767       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4768       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4769     }
4770     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4771     PetscCall(DMPlexGetSupport(dmA, p, &support));
4772     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4773     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4774     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4775     for (s = 0; s < supportSize; ++s) {
4776       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4777     }
4778   }
4779   *equal = PETSC_TRUE;
4780   PetscFunctionReturn(0);
4781 }
4782 
4783 /*@C
4784   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4785 
4786   Not Collective
4787 
4788   Input Parameters:
4789 + dm         - The `DMPLEX`
4790 . cellDim    - The cell dimension
4791 - numCorners - The number of vertices on a cell
4792 
4793   Output Parameters:
4794 . numFaceVertices - The number of vertices on a face
4795 
4796   Level: developer
4797 
4798   Note:
4799   Of course this can only work for a restricted set of symmetric shapes
4800 
4801 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4802 @*/
4803 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4804 {
4805   MPI_Comm comm;
4806 
4807   PetscFunctionBegin;
4808   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4809   PetscValidIntPointer(numFaceVertices, 4);
4810   switch (cellDim) {
4811   case 0:
4812     *numFaceVertices = 0;
4813     break;
4814   case 1:
4815     *numFaceVertices = 1;
4816     break;
4817   case 2:
4818     switch (numCorners) {
4819     case 3:                 /* triangle */
4820       *numFaceVertices = 2; /* Edge has 2 vertices */
4821       break;
4822     case 4:                 /* quadrilateral */
4823       *numFaceVertices = 2; /* Edge has 2 vertices */
4824       break;
4825     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4826       *numFaceVertices = 3; /* Edge has 3 vertices */
4827       break;
4828     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4829       *numFaceVertices = 3; /* Edge has 3 vertices */
4830       break;
4831     default:
4832       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4833     }
4834     break;
4835   case 3:
4836     switch (numCorners) {
4837     case 4:                 /* tetradehdron */
4838       *numFaceVertices = 3; /* Face has 3 vertices */
4839       break;
4840     case 6:                 /* tet cohesive cells */
4841       *numFaceVertices = 4; /* Face has 4 vertices */
4842       break;
4843     case 8:                 /* hexahedron */
4844       *numFaceVertices = 4; /* Face has 4 vertices */
4845       break;
4846     case 9:                 /* tet cohesive Lagrange cells */
4847       *numFaceVertices = 6; /* Face has 6 vertices */
4848       break;
4849     case 10:                /* quadratic tetrahedron */
4850       *numFaceVertices = 6; /* Face has 6 vertices */
4851       break;
4852     case 12:                /* hex cohesive Lagrange cells */
4853       *numFaceVertices = 6; /* Face has 6 vertices */
4854       break;
4855     case 18:                /* quadratic tet cohesive Lagrange cells */
4856       *numFaceVertices = 6; /* Face has 6 vertices */
4857       break;
4858     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4859       *numFaceVertices = 9; /* Face has 9 vertices */
4860       break;
4861     default:
4862       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4863     }
4864     break;
4865   default:
4866     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4867   }
4868   PetscFunctionReturn(0);
4869 }
4870 
4871 /*@
4872   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4873 
4874   Not Collective
4875 
4876   Input Parameter:
4877 . dm    - The `DMPLEX` object
4878 
4879   Output Parameter:
4880 . depthLabel - The `DMLabel` recording point depth
4881 
4882   Level: developer
4883 
4884 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4885 @*/
4886 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4887 {
4888   PetscFunctionBegin;
4889   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4890   PetscValidPointer(depthLabel, 2);
4891   *depthLabel = dm->depthLabel;
4892   PetscFunctionReturn(0);
4893 }
4894 
4895 /*@
4896   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4897 
4898   Not Collective
4899 
4900   Input Parameter:
4901 . dm    - The `DMPLEX` object
4902 
4903   Output Parameter:
4904 . depth - The number of strata (breadth first levels) in the DAG
4905 
4906   Level: developer
4907 
4908   Notes:
4909   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4910 
4911   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4912 
4913   An empty mesh gives -1.
4914 
4915 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4916 @*/
4917 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4918 {
4919   DM_Plex *mesh = (DM_Plex *)dm->data;
4920   DMLabel  label;
4921   PetscInt d = 0;
4922 
4923   PetscFunctionBegin;
4924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4925   PetscValidIntPointer(depth, 2);
4926   if (mesh->tr) {
4927     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4928   } else {
4929     PetscCall(DMPlexGetDepthLabel(dm, &label));
4930     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4931     *depth = d - 1;
4932   }
4933   PetscFunctionReturn(0);
4934 }
4935 
4936 /*@
4937   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4938 
4939   Not Collective
4940 
4941   Input Parameters:
4942 + dm    - The `DMPLEX` object
4943 - depth - The requested depth
4944 
4945   Output Parameters:
4946 + start - The first point at this depth
4947 - end   - One beyond the last point at this depth
4948 
4949   Level: developer
4950 
4951   Notes:
4952   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4953   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
4954   higher dimension, e.g., "edges".
4955 
4956 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4957 @*/
4958 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4959 {
4960   DM_Plex *mesh = (DM_Plex *)dm->data;
4961   DMLabel  label;
4962   PetscInt pStart, pEnd;
4963 
4964   PetscFunctionBegin;
4965   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4966   if (start) {
4967     PetscValidIntPointer(start, 3);
4968     *start = 0;
4969   }
4970   if (end) {
4971     PetscValidIntPointer(end, 4);
4972     *end = 0;
4973   }
4974   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4975   if (pStart == pEnd) PetscFunctionReturn(0);
4976   if (depth < 0) {
4977     if (start) *start = pStart;
4978     if (end) *end = pEnd;
4979     PetscFunctionReturn(0);
4980   }
4981   if (mesh->tr) {
4982     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
4983   } else {
4984     PetscCall(DMPlexGetDepthLabel(dm, &label));
4985     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4986     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4987   }
4988   PetscFunctionReturn(0);
4989 }
4990 
4991 /*@
4992   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4993 
4994   Not Collective
4995 
4996   Input Parameters:
4997 + dm     - The `DMPLEX` object
4998 - height - The requested height
4999 
5000   Output Parameters:
5001 + start - The first point at this height
5002 - end   - One beyond the last point at this height
5003 
5004   Level: developer
5005 
5006   Notes:
5007   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5008   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5009   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5010 
5011 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5012 @*/
5013 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5014 {
5015   DMLabel  label;
5016   PetscInt depth, pStart, pEnd;
5017 
5018   PetscFunctionBegin;
5019   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5020   if (start) {
5021     PetscValidIntPointer(start, 3);
5022     *start = 0;
5023   }
5024   if (end) {
5025     PetscValidIntPointer(end, 4);
5026     *end = 0;
5027   }
5028   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5029   if (pStart == pEnd) PetscFunctionReturn(0);
5030   if (height < 0) {
5031     if (start) *start = pStart;
5032     if (end) *end = pEnd;
5033     PetscFunctionReturn(0);
5034   }
5035   PetscCall(DMPlexGetDepthLabel(dm, &label));
5036   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5037   PetscCall(DMLabelGetNumValues(label, &depth));
5038   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5039   PetscFunctionReturn(0);
5040 }
5041 
5042 /*@
5043   DMPlexGetPointDepth - Get the depth of a given point
5044 
5045   Not Collective
5046 
5047   Input Parameters:
5048 + dm    - The `DMPLEX` object
5049 - point - The point
5050 
5051   Output Parameter:
5052 . depth - The depth of the point
5053 
5054   Level: intermediate
5055 
5056 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5057 @*/
5058 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5059 {
5060   PetscFunctionBegin;
5061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5062   PetscValidIntPointer(depth, 3);
5063   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5064   PetscFunctionReturn(0);
5065 }
5066 
5067 /*@
5068   DMPlexGetPointHeight - Get the height of a given point
5069 
5070   Not Collective
5071 
5072   Input Parameters:
5073 + dm    - The `DMPLEX` object
5074 - point - The point
5075 
5076   Output Parameter:
5077 . height - The height of the point
5078 
5079   Level: intermediate
5080 
5081 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5082 @*/
5083 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5084 {
5085   PetscInt n, pDepth;
5086 
5087   PetscFunctionBegin;
5088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5089   PetscValidIntPointer(height, 3);
5090   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5091   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5092   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5093   PetscFunctionReturn(0);
5094 }
5095 
5096 /*@
5097   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5098 
5099   Not Collective
5100 
5101   Input Parameter:
5102 . dm - The `DMPLEX` object
5103 
5104   Output Parameter:
5105 . celltypeLabel - The `DMLabel` recording cell polytope type
5106 
5107   Level: developer
5108 
5109   Note:
5110   This function will trigger automatica computation of cell types. This can be disabled by calling
5111   `DMCreateLabel`(dm, "celltype") beforehand.
5112 
5113 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5114 @*/
5115 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5116 {
5117   PetscFunctionBegin;
5118   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5119   PetscValidPointer(celltypeLabel, 2);
5120   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5121   *celltypeLabel = dm->celltypeLabel;
5122   PetscFunctionReturn(0);
5123 }
5124 
5125 /*@
5126   DMPlexGetCellType - Get the polytope type of a given cell
5127 
5128   Not Collective
5129 
5130   Input Parameters:
5131 + dm   - The `DMPLEX` object
5132 - cell - The cell
5133 
5134   Output Parameter:
5135 . celltype - The polytope type of the cell
5136 
5137   Level: intermediate
5138 
5139 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5140 @*/
5141 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5142 {
5143   DM_Plex *mesh = (DM_Plex *)dm->data;
5144   DMLabel  label;
5145   PetscInt ct;
5146 
5147   PetscFunctionBegin;
5148   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5149   PetscValidPointer(celltype, 3);
5150   if (mesh->tr) {
5151     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5152   } else {
5153     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5154     PetscCall(DMLabelGetValue(label, cell, &ct));
5155     PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5156     *celltype = (DMPolytopeType)ct;
5157   }
5158   PetscFunctionReturn(0);
5159 }
5160 
5161 /*@
5162   DMPlexSetCellType - Set the polytope type of a given cell
5163 
5164   Not Collective
5165 
5166   Input Parameters:
5167 + dm   - The `DMPLEX` object
5168 . cell - The cell
5169 - celltype - The polytope type of the cell
5170 
5171   Level: advanced
5172 
5173   Note:
5174   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5175   is executed. This function will override the computed type. However, if automatic classification will not succeed
5176   and a user wants to manually specify all types, the classification must be disabled by calling
5177   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5178 
5179 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5180 @*/
5181 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5182 {
5183   DMLabel label;
5184 
5185   PetscFunctionBegin;
5186   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5187   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5188   PetscCall(DMLabelSetValue(label, cell, celltype));
5189   PetscFunctionReturn(0);
5190 }
5191 
5192 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5193 {
5194   PetscSection section, s;
5195   Mat          m;
5196   PetscInt     maxHeight;
5197   const char  *prefix;
5198 
5199   PetscFunctionBegin;
5200   PetscCall(DMClone(dm, cdm));
5201   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5202   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5203   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5204   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5205   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5206   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5207   PetscCall(DMSetLocalSection(*cdm, section));
5208   PetscCall(PetscSectionDestroy(&section));
5209   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5210   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5211   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5212   PetscCall(PetscSectionDestroy(&s));
5213   PetscCall(MatDestroy(&m));
5214 
5215   PetscCall(DMSetNumFields(*cdm, 1));
5216   PetscCall(DMCreateDS(*cdm));
5217   (*cdm)->cloneOpts = PETSC_TRUE;
5218   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5219   PetscFunctionReturn(0);
5220 }
5221 
5222 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5223 {
5224   Vec coordsLocal, cellCoordsLocal;
5225   DM  coordsDM, cellCoordsDM;
5226 
5227   PetscFunctionBegin;
5228   *field = NULL;
5229   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5230   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5231   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5232   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5233   if (coordsLocal && coordsDM) {
5234     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5235     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5236   }
5237   PetscFunctionReturn(0);
5238 }
5239 
5240 /*@C
5241   DMPlexGetConeSection - Return a section which describes the layout of cone data
5242 
5243   Not Collective
5244 
5245   Input Parameters:
5246 . dm        - The `DMPLEX` object
5247 
5248   Output Parameter:
5249 . section - The `PetscSection` object
5250 
5251   Level: developer
5252 
5253 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5254 @*/
5255 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5256 {
5257   DM_Plex *mesh = (DM_Plex *)dm->data;
5258 
5259   PetscFunctionBegin;
5260   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5261   if (section) *section = mesh->coneSection;
5262   PetscFunctionReturn(0);
5263 }
5264 
5265 /*@C
5266   DMPlexGetSupportSection - Return a section which describes the layout of support data
5267 
5268   Not Collective
5269 
5270   Input Parameters:
5271 . dm        - The `DMPLEX` object
5272 
5273   Output Parameter:
5274 . section - The `PetscSection` object
5275 
5276   Level: developer
5277 
5278 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5279 @*/
5280 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5281 {
5282   DM_Plex *mesh = (DM_Plex *)dm->data;
5283 
5284   PetscFunctionBegin;
5285   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5286   if (section) *section = mesh->supportSection;
5287   PetscFunctionReturn(0);
5288 }
5289 
5290 /*@C
5291   DMPlexGetCones - Return cone data
5292 
5293   Not Collective
5294 
5295   Input Parameters:
5296 . dm        - The `DMPLEX` object
5297 
5298   Output Parameter:
5299 . cones - The cone for each point
5300 
5301   Level: developer
5302 
5303 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5304 @*/
5305 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5306 {
5307   DM_Plex *mesh = (DM_Plex *)dm->data;
5308 
5309   PetscFunctionBegin;
5310   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5311   if (cones) *cones = mesh->cones;
5312   PetscFunctionReturn(0);
5313 }
5314 
5315 /*@C
5316   DMPlexGetConeOrientations - Return cone orientation data
5317 
5318   Not Collective
5319 
5320   Input Parameters:
5321 . dm        - The `DMPLEX` object
5322 
5323   Output Parameter:
5324 . coneOrientations - The array of cone orientations for all points
5325 
5326   Level: developer
5327 
5328   Notes:
5329   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5330 
5331   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5332 
5333 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5334 @*/
5335 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5336 {
5337   DM_Plex *mesh = (DM_Plex *)dm->data;
5338 
5339   PetscFunctionBegin;
5340   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5341   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5342   PetscFunctionReturn(0);
5343 }
5344 
5345 /******************************** FEM Support **********************************/
5346 
5347 /*
5348  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5349  representing a line in the section.
5350 */
5351 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5352 {
5353   PetscFunctionBeginHot;
5354   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5355   if (line < 0) {
5356     *k  = 0;
5357     *Nc = 0;
5358   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5359     *k = 1;
5360   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5361     /* An order k SEM disc has k-1 dofs on an edge */
5362     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5363     *k = *k / *Nc + 1;
5364   }
5365   PetscFunctionReturn(0);
5366 }
5367 
5368 /*@
5369 
5370   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5371   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5372   section provided (or the section of the DM).
5373 
5374   Input Parameters:
5375 + dm      - The DM
5376 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5377 - section - The PetscSection to reorder, or NULL for the default section
5378 
5379   Example:
5380   A typical interpolated single-quad mesh might order points as
5381 .vb
5382   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5383 
5384   v4 -- e6 -- v3
5385   |           |
5386   e7    c0    e8
5387   |           |
5388   v1 -- e5 -- v2
5389 .ve
5390 
5391   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5392   dofs in the order of points, e.g.,
5393 .vb
5394     c0 -> [0,1,2,3]
5395     v1 -> [4]
5396     ...
5397     e5 -> [8, 9]
5398 .ve
5399 
5400   which corresponds to the dofs
5401 .vb
5402     6   10  11  7
5403     13  2   3   15
5404     12  0   1   14
5405     4   8   9   5
5406 .ve
5407 
5408   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5409 .vb
5410   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5411 .ve
5412 
5413   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5414 .vb
5415    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5416 .ve
5417 
5418   Level: developer
5419 
5420   Note:
5421   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5422   degree of the basis.
5423 
5424 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5425 @*/
5426 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5427 {
5428   DMLabel   label;
5429   PetscInt  dim, depth = -1, eStart = -1, Nf;
5430   PetscBool vertexchart;
5431 
5432   PetscFunctionBegin;
5433   PetscCall(DMGetDimension(dm, &dim));
5434   if (dim < 1) PetscFunctionReturn(0);
5435   if (point < 0) {
5436     PetscInt sStart, sEnd;
5437 
5438     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5439     point = sEnd - sStart ? sStart : point;
5440   }
5441   PetscCall(DMPlexGetDepthLabel(dm, &label));
5442   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5443   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5444   if (depth == 1) {
5445     eStart = point;
5446   } else if (depth == dim) {
5447     const PetscInt *cone;
5448 
5449     PetscCall(DMPlexGetCone(dm, point, &cone));
5450     if (dim == 2) eStart = cone[0];
5451     else if (dim == 3) {
5452       const PetscInt *cone2;
5453       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5454       eStart = cone2[0];
5455     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " of depth %" PetscInt_FMT " cannot be used to bootstrap spectral ordering for dim %" PetscInt_FMT, point, depth, dim);
5456   } else PetscCheck(depth < 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " of depth %" PetscInt_FMT " cannot be used to bootstrap spectral ordering for dim %" PetscInt_FMT, point, depth, dim);
5457   { /* Determine whether the chart covers all points or just vertices. */
5458     PetscInt pStart, pEnd, cStart, cEnd;
5459     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5460     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5461     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5462     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5463     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5464   }
5465   PetscCall(PetscSectionGetNumFields(section, &Nf));
5466   for (PetscInt d = 1; d <= dim; d++) {
5467     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5468     PetscInt *perm;
5469 
5470     for (f = 0; f < Nf; ++f) {
5471       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5472       size += PetscPowInt(k + 1, d) * Nc;
5473     }
5474     PetscCall(PetscMalloc1(size, &perm));
5475     for (f = 0; f < Nf; ++f) {
5476       switch (d) {
5477       case 1:
5478         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5479         /*
5480          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5481          We want              [ vtx0; edge of length k-1; vtx1 ]
5482          */
5483         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5484         for (i = 0; i < k - 1; i++)
5485           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5486         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5487         foffset = offset;
5488         break;
5489       case 2:
5490         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5491         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5492         /* The SEM order is
5493 
5494          v_lb, {e_b}, v_rb,
5495          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5496          v_lt, reverse {e_t}, v_rt
5497          */
5498         {
5499           const PetscInt of   = 0;
5500           const PetscInt oeb  = of + PetscSqr(k - 1);
5501           const PetscInt oer  = oeb + (k - 1);
5502           const PetscInt oet  = oer + (k - 1);
5503           const PetscInt oel  = oet + (k - 1);
5504           const PetscInt ovlb = oel + (k - 1);
5505           const PetscInt ovrb = ovlb + 1;
5506           const PetscInt ovrt = ovrb + 1;
5507           const PetscInt ovlt = ovrt + 1;
5508           PetscInt       o;
5509 
5510           /* bottom */
5511           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5512           for (o = oeb; o < oer; ++o)
5513             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5514           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5515           /* middle */
5516           for (i = 0; i < k - 1; ++i) {
5517             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5518             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5519               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5520             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5521           }
5522           /* top */
5523           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5524           for (o = oel - 1; o >= oet; --o)
5525             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5526           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5527           foffset = offset;
5528         }
5529         break;
5530       case 3:
5531         /* The original hex closure is
5532 
5533          {c,
5534          f_b, f_t, f_f, f_b, f_r, f_l,
5535          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5536          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5537          */
5538         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5539         /* The SEM order is
5540          Bottom Slice
5541          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5542          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5543          v_blb, {e_bb}, v_brb,
5544 
5545          Middle Slice (j)
5546          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5547          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5548          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5549 
5550          Top Slice
5551          v_tlf, {e_tf}, v_trf,
5552          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5553          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5554          */
5555         {
5556           const PetscInt oc    = 0;
5557           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5558           const PetscInt oft   = ofb + PetscSqr(k - 1);
5559           const PetscInt off   = oft + PetscSqr(k - 1);
5560           const PetscInt ofk   = off + PetscSqr(k - 1);
5561           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5562           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5563           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5564           const PetscInt oebb  = oebl + (k - 1);
5565           const PetscInt oebr  = oebb + (k - 1);
5566           const PetscInt oebf  = oebr + (k - 1);
5567           const PetscInt oetf  = oebf + (k - 1);
5568           const PetscInt oetr  = oetf + (k - 1);
5569           const PetscInt oetb  = oetr + (k - 1);
5570           const PetscInt oetl  = oetb + (k - 1);
5571           const PetscInt oerf  = oetl + (k - 1);
5572           const PetscInt oelf  = oerf + (k - 1);
5573           const PetscInt oelb  = oelf + (k - 1);
5574           const PetscInt oerb  = oelb + (k - 1);
5575           const PetscInt ovblf = oerb + (k - 1);
5576           const PetscInt ovblb = ovblf + 1;
5577           const PetscInt ovbrb = ovblb + 1;
5578           const PetscInt ovbrf = ovbrb + 1;
5579           const PetscInt ovtlf = ovbrf + 1;
5580           const PetscInt ovtrf = ovtlf + 1;
5581           const PetscInt ovtrb = ovtrf + 1;
5582           const PetscInt ovtlb = ovtrb + 1;
5583           PetscInt       o, n;
5584 
5585           /* Bottom Slice */
5586           /*   bottom */
5587           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5588           for (o = oetf - 1; o >= oebf; --o)
5589             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5590           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5591           /*   middle */
5592           for (i = 0; i < k - 1; ++i) {
5593             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5594             for (n = 0; n < k - 1; ++n) {
5595               o = ofb + n * (k - 1) + i;
5596               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5597             }
5598             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5599           }
5600           /*   top */
5601           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5602           for (o = oebb; o < oebr; ++o)
5603             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5604           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5605 
5606           /* Middle Slice */
5607           for (j = 0; j < k - 1; ++j) {
5608             /*   bottom */
5609             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5610             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5611               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5612             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5613             /*   middle */
5614             for (i = 0; i < k - 1; ++i) {
5615               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5616               for (n = 0; n < k - 1; ++n)
5617                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5618               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5619             }
5620             /*   top */
5621             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5622             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5623               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5624             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5625           }
5626 
5627           /* Top Slice */
5628           /*   bottom */
5629           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5630           for (o = oetf; o < oetr; ++o)
5631             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5632           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5633           /*   middle */
5634           for (i = 0; i < k - 1; ++i) {
5635             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5636             for (n = 0; n < k - 1; ++n)
5637               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5638             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5639           }
5640           /*   top */
5641           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5642           for (o = oetl - 1; o >= oetb; --o)
5643             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5644           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5645 
5646           foffset = offset;
5647         }
5648         break;
5649       default:
5650         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5651       }
5652     }
5653     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5654     /* Check permutation */
5655     {
5656       PetscInt *check;
5657 
5658       PetscCall(PetscMalloc1(size, &check));
5659       for (i = 0; i < size; ++i) {
5660         check[i] = -1;
5661         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5662       }
5663       for (i = 0; i < size; ++i) check[perm[i]] = i;
5664       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5665       PetscCall(PetscFree(check));
5666     }
5667     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5668     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5669       PetscInt *loc_perm;
5670       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5671       for (PetscInt i = 0; i < size; i++) {
5672         loc_perm[i]        = perm[i];
5673         loc_perm[size + i] = size + perm[i];
5674       }
5675       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5676     }
5677   }
5678   PetscFunctionReturn(0);
5679 }
5680 
5681 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5682 {
5683   PetscDS  prob;
5684   PetscInt depth, Nf, h;
5685   DMLabel  label;
5686 
5687   PetscFunctionBeginHot;
5688   PetscCall(DMGetDS(dm, &prob));
5689   Nf      = prob->Nf;
5690   label   = dm->depthLabel;
5691   *dspace = NULL;
5692   if (field < Nf) {
5693     PetscObject disc = prob->disc[field];
5694 
5695     if (disc->classid == PETSCFE_CLASSID) {
5696       PetscDualSpace dsp;
5697 
5698       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5699       PetscCall(DMLabelGetNumValues(label, &depth));
5700       PetscCall(DMLabelGetValue(label, point, &h));
5701       h = depth - 1 - h;
5702       if (h) {
5703         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5704       } else {
5705         *dspace = dsp;
5706       }
5707     }
5708   }
5709   PetscFunctionReturn(0);
5710 }
5711 
5712 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5713 {
5714   PetscScalar       *array;
5715   const PetscScalar *vArray;
5716   const PetscInt    *cone, *coneO;
5717   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5718 
5719   PetscFunctionBeginHot;
5720   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5721   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5722   PetscCall(DMPlexGetCone(dm, point, &cone));
5723   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5724   if (!values || !*values) {
5725     if ((point >= pStart) && (point < pEnd)) {
5726       PetscInt dof;
5727 
5728       PetscCall(PetscSectionGetDof(section, point, &dof));
5729       size += dof;
5730     }
5731     for (p = 0; p < numPoints; ++p) {
5732       const PetscInt cp = cone[p];
5733       PetscInt       dof;
5734 
5735       if ((cp < pStart) || (cp >= pEnd)) continue;
5736       PetscCall(PetscSectionGetDof(section, cp, &dof));
5737       size += dof;
5738     }
5739     if (!values) {
5740       if (csize) *csize = size;
5741       PetscFunctionReturn(0);
5742     }
5743     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5744   } else {
5745     array = *values;
5746   }
5747   size = 0;
5748   PetscCall(VecGetArrayRead(v, &vArray));
5749   if ((point >= pStart) && (point < pEnd)) {
5750     PetscInt           dof, off, d;
5751     const PetscScalar *varr;
5752 
5753     PetscCall(PetscSectionGetDof(section, point, &dof));
5754     PetscCall(PetscSectionGetOffset(section, point, &off));
5755     varr = &vArray[off];
5756     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5757     size += dof;
5758   }
5759   for (p = 0; p < numPoints; ++p) {
5760     const PetscInt     cp = cone[p];
5761     PetscInt           o  = coneO[p];
5762     PetscInt           dof, off, d;
5763     const PetscScalar *varr;
5764 
5765     if ((cp < pStart) || (cp >= pEnd)) continue;
5766     PetscCall(PetscSectionGetDof(section, cp, &dof));
5767     PetscCall(PetscSectionGetOffset(section, cp, &off));
5768     varr = &vArray[off];
5769     if (o >= 0) {
5770       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5771     } else {
5772       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5773     }
5774     size += dof;
5775   }
5776   PetscCall(VecRestoreArrayRead(v, &vArray));
5777   if (!*values) {
5778     if (csize) *csize = size;
5779     *values = array;
5780   } else {
5781     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5782     *csize = size;
5783   }
5784   PetscFunctionReturn(0);
5785 }
5786 
5787 /* Compress out points not in the section */
5788 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5789 {
5790   const PetscInt np = *numPoints;
5791   PetscInt       pStart, pEnd, p, q;
5792 
5793   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5794   for (p = 0, q = 0; p < np; ++p) {
5795     const PetscInt r = points[p * 2];
5796     if ((r >= pStart) && (r < pEnd)) {
5797       points[q * 2]     = r;
5798       points[q * 2 + 1] = points[p * 2 + 1];
5799       ++q;
5800     }
5801   }
5802   *numPoints = q;
5803   return 0;
5804 }
5805 
5806 /* Compressed closure does not apply closure permutation */
5807 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5808 {
5809   const PetscInt *cla = NULL;
5810   PetscInt        np, *pts = NULL;
5811 
5812   PetscFunctionBeginHot;
5813   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5814   if (*clPoints) {
5815     PetscInt dof, off;
5816 
5817     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5818     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5819     PetscCall(ISGetIndices(*clPoints, &cla));
5820     np  = dof / 2;
5821     pts = (PetscInt *)&cla[off];
5822   } else {
5823     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5824     PetscCall(CompressPoints_Private(section, &np, pts));
5825   }
5826   *numPoints = np;
5827   *points    = pts;
5828   *clp       = cla;
5829   PetscFunctionReturn(0);
5830 }
5831 
5832 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5833 {
5834   PetscFunctionBeginHot;
5835   if (!*clPoints) {
5836     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5837   } else {
5838     PetscCall(ISRestoreIndices(*clPoints, clp));
5839   }
5840   *numPoints = 0;
5841   *points    = NULL;
5842   *clSec     = NULL;
5843   *clPoints  = NULL;
5844   *clp       = NULL;
5845   PetscFunctionReturn(0);
5846 }
5847 
5848 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5849 {
5850   PetscInt            offset = 0, p;
5851   const PetscInt    **perms  = NULL;
5852   const PetscScalar **flips  = NULL;
5853 
5854   PetscFunctionBeginHot;
5855   *size = 0;
5856   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5857   for (p = 0; p < numPoints; p++) {
5858     const PetscInt     point = points[2 * p];
5859     const PetscInt    *perm  = perms ? perms[p] : NULL;
5860     const PetscScalar *flip  = flips ? flips[p] : NULL;
5861     PetscInt           dof, off, d;
5862     const PetscScalar *varr;
5863 
5864     PetscCall(PetscSectionGetDof(section, point, &dof));
5865     PetscCall(PetscSectionGetOffset(section, point, &off));
5866     varr = &vArray[off];
5867     if (clperm) {
5868       if (perm) {
5869         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5870       } else {
5871         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5872       }
5873       if (flip) {
5874         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5875       }
5876     } else {
5877       if (perm) {
5878         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5879       } else {
5880         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5881       }
5882       if (flip) {
5883         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5884       }
5885     }
5886     offset += dof;
5887   }
5888   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5889   *size = offset;
5890   PetscFunctionReturn(0);
5891 }
5892 
5893 static inline PetscErrorCode DMPlexVecGetClosure_Fields_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], PetscInt numFields, const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5894 {
5895   PetscInt offset = 0, f;
5896 
5897   PetscFunctionBeginHot;
5898   *size = 0;
5899   for (f = 0; f < numFields; ++f) {
5900     PetscInt            p;
5901     const PetscInt    **perms = NULL;
5902     const PetscScalar **flips = NULL;
5903 
5904     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5905     for (p = 0; p < numPoints; p++) {
5906       const PetscInt     point = points[2 * p];
5907       PetscInt           fdof, foff, b;
5908       const PetscScalar *varr;
5909       const PetscInt    *perm = perms ? perms[p] : NULL;
5910       const PetscScalar *flip = flips ? flips[p] : NULL;
5911 
5912       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5913       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5914       varr = &vArray[foff];
5915       if (clperm) {
5916         if (perm) {
5917           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5918         } else {
5919           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5920         }
5921         if (flip) {
5922           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5923         }
5924       } else {
5925         if (perm) {
5926           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5927         } else {
5928           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5929         }
5930         if (flip) {
5931           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5932         }
5933       }
5934       offset += fdof;
5935     }
5936     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5937   }
5938   *size = offset;
5939   PetscFunctionReturn(0);
5940 }
5941 
5942 /*@C
5943   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5944 
5945   Not collective
5946 
5947   Input Parameters:
5948 + dm - The `DM`
5949 . section - The section describing the layout in v, or NULL to use the default section
5950 . v - The local vector
5951 - point - The point in the `DM`
5952 
5953   Input/Output Parameters:
5954 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5955 - values - An array to use for the values, or NULL to have it allocated automatically;
5956            if the user provided NULL, it is a borrowed array and should not be freed
5957 
5958   Level: intermediate
5959 
5960   Notes:
5961   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to NULL in the
5962   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
5963   assembly function, and a user may already have allocated storage for this operation.
5964 
5965   A typical use could be
5966 .vb
5967    values = NULL;
5968    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5969    for (cl = 0; cl < clSize; ++cl) {
5970      <Compute on closure>
5971    }
5972    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5973 .ve
5974   or
5975 .vb
5976    PetscMalloc1(clMaxSize, &values);
5977    for (p = pStart; p < pEnd; ++p) {
5978      clSize = clMaxSize;
5979      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5980      for (cl = 0; cl < clSize; ++cl) {
5981        <Compute on closure>
5982      }
5983    }
5984    PetscFree(values);
5985 .ve
5986 
5987   Fortran Note:
5988   The csize argument is not present in the Fortran binding since it is internal to the array.
5989 
5990 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5991 @*/
5992 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5993 {
5994   PetscSection    clSection;
5995   IS              clPoints;
5996   PetscInt       *points = NULL;
5997   const PetscInt *clp, *perm;
5998   PetscInt        depth, numFields, numPoints, asize;
5999 
6000   PetscFunctionBeginHot;
6001   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6002   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6003   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6004   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6005   PetscCall(DMPlexGetDepth(dm, &depth));
6006   PetscCall(PetscSectionGetNumFields(section, &numFields));
6007   if (depth == 1 && numFields < 2) {
6008     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6009     PetscFunctionReturn(0);
6010   }
6011   /* Get points */
6012   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6013   /* Get sizes */
6014   asize = 0;
6015   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6016     PetscInt dof;
6017     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6018     asize += dof;
6019   }
6020   if (values) {
6021     const PetscScalar *vArray;
6022     PetscInt           size;
6023 
6024     if (*values) {
6025       PetscCheck(*csize >= asize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Provided array size %" PetscInt_FMT " not sufficient to hold closure size %" PetscInt_FMT, *csize, asize);
6026     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6027     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6028     PetscCall(VecGetArrayRead(v, &vArray));
6029     /* Get values */
6030     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6031     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6032     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6033     /* Cleanup array */
6034     PetscCall(VecRestoreArrayRead(v, &vArray));
6035   }
6036   if (csize) *csize = asize;
6037   /* Cleanup points */
6038   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6039   PetscFunctionReturn(0);
6040 }
6041 
6042 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6043 {
6044   DMLabel            depthLabel;
6045   PetscSection       clSection;
6046   IS                 clPoints;
6047   PetscScalar       *array;
6048   const PetscScalar *vArray;
6049   PetscInt          *points = NULL;
6050   const PetscInt    *clp, *perm = NULL;
6051   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6052 
6053   PetscFunctionBeginHot;
6054   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6055   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6056   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6057   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6058   PetscCall(DMPlexGetDepth(dm, &mdepth));
6059   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6060   PetscCall(PetscSectionGetNumFields(section, &numFields));
6061   if (mdepth == 1 && numFields < 2) {
6062     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6063     PetscFunctionReturn(0);
6064   }
6065   /* Get points */
6066   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6067   for (clsize = 0, p = 0; p < Np; p++) {
6068     PetscInt dof;
6069     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6070     clsize += dof;
6071   }
6072   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6073   /* Filter points */
6074   for (p = 0; p < numPoints * 2; p += 2) {
6075     PetscInt dep;
6076 
6077     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6078     if (dep != depth) continue;
6079     points[Np * 2 + 0] = points[p];
6080     points[Np * 2 + 1] = points[p + 1];
6081     ++Np;
6082   }
6083   /* Get array */
6084   if (!values || !*values) {
6085     PetscInt asize = 0, dof;
6086 
6087     for (p = 0; p < Np * 2; p += 2) {
6088       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6089       asize += dof;
6090     }
6091     if (!values) {
6092       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6093       if (csize) *csize = asize;
6094       PetscFunctionReturn(0);
6095     }
6096     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6097   } else {
6098     array = *values;
6099   }
6100   PetscCall(VecGetArrayRead(v, &vArray));
6101   /* Get values */
6102   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6103   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6104   /* Cleanup points */
6105   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6106   /* Cleanup array */
6107   PetscCall(VecRestoreArrayRead(v, &vArray));
6108   if (!*values) {
6109     if (csize) *csize = size;
6110     *values = array;
6111   } else {
6112     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6113     *csize = size;
6114   }
6115   PetscFunctionReturn(0);
6116 }
6117 
6118 /*@C
6119   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6120 
6121   Not collective
6122 
6123   Input Parameters:
6124 + dm - The `DM`
6125 . section - The section describing the layout in v, or NULL to use the default section
6126 . v - The local vector
6127 . point - The point in the `DM`
6128 . csize - The number of values in the closure, or NULL
6129 - values - The array of values, which is a borrowed array and should not be freed
6130 
6131   Level: intermediate
6132 
6133   Note:
6134   The array values are discarded and not copied back into v. In order to copy values back to v, use `DMPlexVecSetClosure()`
6135 
6136   Fortran Note:
6137   The csize argument is not present in the Fortran binding since it is internal to the array.
6138 
6139 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6140 @*/
6141 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6142 {
6143   PetscInt size = 0;
6144 
6145   PetscFunctionBegin;
6146   /* Should work without recalculating size */
6147   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6148   *values = NULL;
6149   PetscFunctionReturn(0);
6150 }
6151 
6152 static inline void add(PetscScalar *x, PetscScalar y)
6153 {
6154   *x += y;
6155 }
6156 static inline void insert(PetscScalar *x, PetscScalar y)
6157 {
6158   *x = y;
6159 }
6160 
6161 static inline PetscErrorCode updatePoint_private(PetscSection section, PetscInt point, PetscInt dof, void (*fuse)(PetscScalar *, PetscScalar), PetscBool setBC, const PetscInt perm[], const PetscScalar flip[], const PetscInt clperm[], const PetscScalar values[], PetscInt offset, PetscScalar array[])
6162 {
6163   PetscInt        cdof;  /* The number of constraints on this point */
6164   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6165   PetscScalar    *a;
6166   PetscInt        off, cind = 0, k;
6167 
6168   PetscFunctionBegin;
6169   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6170   PetscCall(PetscSectionGetOffset(section, point, &off));
6171   a = &array[off];
6172   if (!cdof || setBC) {
6173     if (clperm) {
6174       if (perm) {
6175         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6176       } else {
6177         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6178       }
6179     } else {
6180       if (perm) {
6181         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6182       } else {
6183         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6184       }
6185     }
6186   } else {
6187     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6188     if (clperm) {
6189       if (perm) {
6190         for (k = 0; k < dof; ++k) {
6191           if ((cind < cdof) && (k == cdofs[cind])) {
6192             ++cind;
6193             continue;
6194           }
6195           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6196         }
6197       } else {
6198         for (k = 0; k < dof; ++k) {
6199           if ((cind < cdof) && (k == cdofs[cind])) {
6200             ++cind;
6201             continue;
6202           }
6203           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6204         }
6205       }
6206     } else {
6207       if (perm) {
6208         for (k = 0; k < dof; ++k) {
6209           if ((cind < cdof) && (k == cdofs[cind])) {
6210             ++cind;
6211             continue;
6212           }
6213           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6214         }
6215       } else {
6216         for (k = 0; k < dof; ++k) {
6217           if ((cind < cdof) && (k == cdofs[cind])) {
6218             ++cind;
6219             continue;
6220           }
6221           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6222         }
6223       }
6224     }
6225   }
6226   PetscFunctionReturn(0);
6227 }
6228 
6229 static inline PetscErrorCode updatePointBC_private(PetscSection section, PetscInt point, PetscInt dof, void (*fuse)(PetscScalar *, PetscScalar), const PetscInt perm[], const PetscScalar flip[], const PetscInt clperm[], const PetscScalar values[], PetscInt offset, PetscScalar array[])
6230 {
6231   PetscInt        cdof;  /* The number of constraints on this point */
6232   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6233   PetscScalar    *a;
6234   PetscInt        off, cind = 0, k;
6235 
6236   PetscFunctionBegin;
6237   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6238   PetscCall(PetscSectionGetOffset(section, point, &off));
6239   a = &array[off];
6240   if (cdof) {
6241     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6242     if (clperm) {
6243       if (perm) {
6244         for (k = 0; k < dof; ++k) {
6245           if ((cind < cdof) && (k == cdofs[cind])) {
6246             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6247             cind++;
6248           }
6249         }
6250       } else {
6251         for (k = 0; k < dof; ++k) {
6252           if ((cind < cdof) && (k == cdofs[cind])) {
6253             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6254             cind++;
6255           }
6256         }
6257       }
6258     } else {
6259       if (perm) {
6260         for (k = 0; k < dof; ++k) {
6261           if ((cind < cdof) && (k == cdofs[cind])) {
6262             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6263             cind++;
6264           }
6265         }
6266       } else {
6267         for (k = 0; k < dof; ++k) {
6268           if ((cind < cdof) && (k == cdofs[cind])) {
6269             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6270             cind++;
6271           }
6272         }
6273       }
6274     }
6275   }
6276   PetscFunctionReturn(0);
6277 }
6278 
6279 static inline PetscErrorCode updatePointFields_private(PetscSection section, PetscInt point, const PetscInt *perm, const PetscScalar *flip, PetscInt f, void (*fuse)(PetscScalar *, PetscScalar), PetscBool setBC, const PetscInt clperm[], const PetscScalar values[], PetscInt *offset, PetscScalar array[])
6280 {
6281   PetscScalar    *a;
6282   PetscInt        fdof, foff, fcdof, foffset = *offset;
6283   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6284   PetscInt        cind = 0, b;
6285 
6286   PetscFunctionBegin;
6287   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6288   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6289   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6290   a = &array[foff];
6291   if (!fcdof || setBC) {
6292     if (clperm) {
6293       if (perm) {
6294         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6295       } else {
6296         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6297       }
6298     } else {
6299       if (perm) {
6300         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6301       } else {
6302         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6303       }
6304     }
6305   } else {
6306     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6307     if (clperm) {
6308       if (perm) {
6309         for (b = 0; b < fdof; b++) {
6310           if ((cind < fcdof) && (b == fcdofs[cind])) {
6311             ++cind;
6312             continue;
6313           }
6314           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6315         }
6316       } else {
6317         for (b = 0; b < fdof; b++) {
6318           if ((cind < fcdof) && (b == fcdofs[cind])) {
6319             ++cind;
6320             continue;
6321           }
6322           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6323         }
6324       }
6325     } else {
6326       if (perm) {
6327         for (b = 0; b < fdof; b++) {
6328           if ((cind < fcdof) && (b == fcdofs[cind])) {
6329             ++cind;
6330             continue;
6331           }
6332           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6333         }
6334       } else {
6335         for (b = 0; b < fdof; b++) {
6336           if ((cind < fcdof) && (b == fcdofs[cind])) {
6337             ++cind;
6338             continue;
6339           }
6340           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6341         }
6342       }
6343     }
6344   }
6345   *offset += fdof;
6346   PetscFunctionReturn(0);
6347 }
6348 
6349 static inline PetscErrorCode updatePointFieldsBC_private(PetscSection section, PetscInt point, const PetscInt perm[], const PetscScalar flip[], PetscInt f, PetscInt Ncc, const PetscInt comps[], void (*fuse)(PetscScalar *, PetscScalar), const PetscInt clperm[], const PetscScalar values[], PetscInt *offset, PetscScalar array[])
6350 {
6351   PetscScalar    *a;
6352   PetscInt        fdof, foff, fcdof, foffset = *offset;
6353   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6354   PetscInt        Nc, cind = 0, ncind = 0, b;
6355   PetscBool       ncSet, fcSet;
6356 
6357   PetscFunctionBegin;
6358   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6359   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6360   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6361   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6362   a = &array[foff];
6363   if (fcdof) {
6364     /* We just override fcdof and fcdofs with Ncc and comps */
6365     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6366     if (clperm) {
6367       if (perm) {
6368         if (comps) {
6369           for (b = 0; b < fdof; b++) {
6370             ncSet = fcSet = PETSC_FALSE;
6371             if (b % Nc == comps[ncind]) {
6372               ncind = (ncind + 1) % Ncc;
6373               ncSet = PETSC_TRUE;
6374             }
6375             if ((cind < fcdof) && (b == fcdofs[cind])) {
6376               ++cind;
6377               fcSet = PETSC_TRUE;
6378             }
6379             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6380           }
6381         } else {
6382           for (b = 0; b < fdof; b++) {
6383             if ((cind < fcdof) && (b == fcdofs[cind])) {
6384               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6385               ++cind;
6386             }
6387           }
6388         }
6389       } else {
6390         if (comps) {
6391           for (b = 0; b < fdof; b++) {
6392             ncSet = fcSet = PETSC_FALSE;
6393             if (b % Nc == comps[ncind]) {
6394               ncind = (ncind + 1) % Ncc;
6395               ncSet = PETSC_TRUE;
6396             }
6397             if ((cind < fcdof) && (b == fcdofs[cind])) {
6398               ++cind;
6399               fcSet = PETSC_TRUE;
6400             }
6401             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6402           }
6403         } else {
6404           for (b = 0; b < fdof; b++) {
6405             if ((cind < fcdof) && (b == fcdofs[cind])) {
6406               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6407               ++cind;
6408             }
6409           }
6410         }
6411       }
6412     } else {
6413       if (perm) {
6414         if (comps) {
6415           for (b = 0; b < fdof; b++) {
6416             ncSet = fcSet = PETSC_FALSE;
6417             if (b % Nc == comps[ncind]) {
6418               ncind = (ncind + 1) % Ncc;
6419               ncSet = PETSC_TRUE;
6420             }
6421             if ((cind < fcdof) && (b == fcdofs[cind])) {
6422               ++cind;
6423               fcSet = PETSC_TRUE;
6424             }
6425             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6426           }
6427         } else {
6428           for (b = 0; b < fdof; b++) {
6429             if ((cind < fcdof) && (b == fcdofs[cind])) {
6430               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6431               ++cind;
6432             }
6433           }
6434         }
6435       } else {
6436         if (comps) {
6437           for (b = 0; b < fdof; b++) {
6438             ncSet = fcSet = PETSC_FALSE;
6439             if (b % Nc == comps[ncind]) {
6440               ncind = (ncind + 1) % Ncc;
6441               ncSet = PETSC_TRUE;
6442             }
6443             if ((cind < fcdof) && (b == fcdofs[cind])) {
6444               ++cind;
6445               fcSet = PETSC_TRUE;
6446             }
6447             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6448           }
6449         } else {
6450           for (b = 0; b < fdof; b++) {
6451             if ((cind < fcdof) && (b == fcdofs[cind])) {
6452               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6453               ++cind;
6454             }
6455           }
6456         }
6457       }
6458     }
6459   }
6460   *offset += fdof;
6461   PetscFunctionReturn(0);
6462 }
6463 
6464 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6465 {
6466   PetscScalar    *array;
6467   const PetscInt *cone, *coneO;
6468   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6469 
6470   PetscFunctionBeginHot;
6471   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6472   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6473   PetscCall(DMPlexGetCone(dm, point, &cone));
6474   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6475   PetscCall(VecGetArray(v, &array));
6476   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6477     const PetscInt cp = !p ? point : cone[p - 1];
6478     const PetscInt o  = !p ? 0 : coneO[p - 1];
6479 
6480     if ((cp < pStart) || (cp >= pEnd)) {
6481       dof = 0;
6482       continue;
6483     }
6484     PetscCall(PetscSectionGetDof(section, cp, &dof));
6485     /* ADD_VALUES */
6486     {
6487       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6488       PetscScalar    *a;
6489       PetscInt        cdof, coff, cind = 0, k;
6490 
6491       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6492       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6493       a = &array[coff];
6494       if (!cdof) {
6495         if (o >= 0) {
6496           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6497         } else {
6498           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6499         }
6500       } else {
6501         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6502         if (o >= 0) {
6503           for (k = 0; k < dof; ++k) {
6504             if ((cind < cdof) && (k == cdofs[cind])) {
6505               ++cind;
6506               continue;
6507             }
6508             a[k] += values[off + k];
6509           }
6510         } else {
6511           for (k = 0; k < dof; ++k) {
6512             if ((cind < cdof) && (k == cdofs[cind])) {
6513               ++cind;
6514               continue;
6515             }
6516             a[k] += values[off + dof - k - 1];
6517           }
6518         }
6519       }
6520     }
6521   }
6522   PetscCall(VecRestoreArray(v, &array));
6523   PetscFunctionReturn(0);
6524 }
6525 
6526 /*@C
6527   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6528 
6529   Not collective
6530 
6531   Input Parameters:
6532 + dm - The `DM`
6533 . section - The section describing the layout in v, or NULL to use the default section
6534 . v - The local vector
6535 . point - The point in the DM
6536 . values - The array of values
6537 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6538          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6539 
6540   Level: intermediate
6541 
6542 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6543 @*/
6544 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6545 {
6546   PetscSection    clSection;
6547   IS              clPoints;
6548   PetscScalar    *array;
6549   PetscInt       *points = NULL;
6550   const PetscInt *clp, *clperm = NULL;
6551   PetscInt        depth, numFields, numPoints, p, clsize;
6552 
6553   PetscFunctionBeginHot;
6554   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6555   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6556   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6557   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6558   PetscCall(DMPlexGetDepth(dm, &depth));
6559   PetscCall(PetscSectionGetNumFields(section, &numFields));
6560   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6561     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6562     PetscFunctionReturn(0);
6563   }
6564   /* Get points */
6565   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6566   for (clsize = 0, p = 0; p < numPoints; p++) {
6567     PetscInt dof;
6568     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6569     clsize += dof;
6570   }
6571   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6572   /* Get array */
6573   PetscCall(VecGetArray(v, &array));
6574   /* Get values */
6575   if (numFields > 0) {
6576     PetscInt offset = 0, f;
6577     for (f = 0; f < numFields; ++f) {
6578       const PetscInt    **perms = NULL;
6579       const PetscScalar **flips = NULL;
6580 
6581       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6582       switch (mode) {
6583       case INSERT_VALUES:
6584         for (p = 0; p < numPoints; p++) {
6585           const PetscInt     point = points[2 * p];
6586           const PetscInt    *perm  = perms ? perms[p] : NULL;
6587           const PetscScalar *flip  = flips ? flips[p] : NULL;
6588           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6589         }
6590         break;
6591       case INSERT_ALL_VALUES:
6592         for (p = 0; p < numPoints; p++) {
6593           const PetscInt     point = points[2 * p];
6594           const PetscInt    *perm  = perms ? perms[p] : NULL;
6595           const PetscScalar *flip  = flips ? flips[p] : NULL;
6596           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6597         }
6598         break;
6599       case INSERT_BC_VALUES:
6600         for (p = 0; p < numPoints; p++) {
6601           const PetscInt     point = points[2 * p];
6602           const PetscInt    *perm  = perms ? perms[p] : NULL;
6603           const PetscScalar *flip  = flips ? flips[p] : NULL;
6604           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6605         }
6606         break;
6607       case ADD_VALUES:
6608         for (p = 0; p < numPoints; p++) {
6609           const PetscInt     point = points[2 * p];
6610           const PetscInt    *perm  = perms ? perms[p] : NULL;
6611           const PetscScalar *flip  = flips ? flips[p] : NULL;
6612           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6613         }
6614         break;
6615       case ADD_ALL_VALUES:
6616         for (p = 0; p < numPoints; p++) {
6617           const PetscInt     point = points[2 * p];
6618           const PetscInt    *perm  = perms ? perms[p] : NULL;
6619           const PetscScalar *flip  = flips ? flips[p] : NULL;
6620           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6621         }
6622         break;
6623       case ADD_BC_VALUES:
6624         for (p = 0; p < numPoints; p++) {
6625           const PetscInt     point = points[2 * p];
6626           const PetscInt    *perm  = perms ? perms[p] : NULL;
6627           const PetscScalar *flip  = flips ? flips[p] : NULL;
6628           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6629         }
6630         break;
6631       default:
6632         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6633       }
6634       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6635     }
6636   } else {
6637     PetscInt            dof, off;
6638     const PetscInt    **perms = NULL;
6639     const PetscScalar **flips = NULL;
6640 
6641     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6642     switch (mode) {
6643     case INSERT_VALUES:
6644       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6645         const PetscInt     point = points[2 * p];
6646         const PetscInt    *perm  = perms ? perms[p] : NULL;
6647         const PetscScalar *flip  = flips ? flips[p] : NULL;
6648         PetscCall(PetscSectionGetDof(section, point, &dof));
6649         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6650       }
6651       break;
6652     case INSERT_ALL_VALUES:
6653       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6654         const PetscInt     point = points[2 * p];
6655         const PetscInt    *perm  = perms ? perms[p] : NULL;
6656         const PetscScalar *flip  = flips ? flips[p] : NULL;
6657         PetscCall(PetscSectionGetDof(section, point, &dof));
6658         updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array);
6659       }
6660       break;
6661     case INSERT_BC_VALUES:
6662       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6663         const PetscInt     point = points[2 * p];
6664         const PetscInt    *perm  = perms ? perms[p] : NULL;
6665         const PetscScalar *flip  = flips ? flips[p] : NULL;
6666         PetscCall(PetscSectionGetDof(section, point, &dof));
6667         updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array);
6668       }
6669       break;
6670     case ADD_VALUES:
6671       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6672         const PetscInt     point = points[2 * p];
6673         const PetscInt    *perm  = perms ? perms[p] : NULL;
6674         const PetscScalar *flip  = flips ? flips[p] : NULL;
6675         PetscCall(PetscSectionGetDof(section, point, &dof));
6676         updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array);
6677       }
6678       break;
6679     case ADD_ALL_VALUES:
6680       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6681         const PetscInt     point = points[2 * p];
6682         const PetscInt    *perm  = perms ? perms[p] : NULL;
6683         const PetscScalar *flip  = flips ? flips[p] : NULL;
6684         PetscCall(PetscSectionGetDof(section, point, &dof));
6685         updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array);
6686       }
6687       break;
6688     case ADD_BC_VALUES:
6689       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6690         const PetscInt     point = points[2 * p];
6691         const PetscInt    *perm  = perms ? perms[p] : NULL;
6692         const PetscScalar *flip  = flips ? flips[p] : NULL;
6693         PetscCall(PetscSectionGetDof(section, point, &dof));
6694         updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array);
6695       }
6696       break;
6697     default:
6698       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6699     }
6700     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6701   }
6702   /* Cleanup points */
6703   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6704   /* Cleanup array */
6705   PetscCall(VecRestoreArray(v, &array));
6706   PetscFunctionReturn(0);
6707 }
6708 
6709 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6710 {
6711   const PetscInt *supp, *cone;
6712   PetscScalar    *a;
6713   PetscInt        dim, Ns, dof, off, n = 0;
6714 
6715   PetscFunctionBegin;
6716   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6717   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6718   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6719   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6720   if (PetscDefined(USE_DEBUG)) {
6721     PetscInt vStart, vEnd;
6722 
6723     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6724     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);
6725   }
6726   PetscValidScalarPointer(values, 5);
6727 
6728   PetscCall(DMGetDimension(dm, &dim));
6729   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6730   PetscCall(DMPlexGetSupport(dm, point, &supp));
6731   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);
6732   PetscCall(VecGetArray(v, &a));
6733   PetscCall(PetscSectionGetDof(section, point, &dof));
6734   PetscCall(PetscSectionGetOffset(section, point, &off));
6735   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6736   for (PetscInt d = 0; d < dim; ++d) {
6737     // Left edge
6738     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6739     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6740     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6741     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6742     // Right edge
6743     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6744     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6745     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6746     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6747   }
6748   PetscCall(VecRestoreArray(v, &a));
6749   PetscFunctionReturn(0);
6750 }
6751 
6752 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6753 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6754 {
6755   PetscFunctionBegin;
6756   *contains = PETSC_TRUE;
6757   if (label) {
6758     PetscInt fdof;
6759 
6760     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6761     if (!*contains) {
6762       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6763       *offset += fdof;
6764       PetscFunctionReturn(0);
6765     }
6766   }
6767   PetscFunctionReturn(0);
6768 }
6769 
6770 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6771 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)
6772 {
6773   PetscSection    clSection;
6774   IS              clPoints;
6775   PetscScalar    *array;
6776   PetscInt       *points = NULL;
6777   const PetscInt *clp;
6778   PetscInt        numFields, numPoints, p;
6779   PetscInt        offset = 0, f;
6780 
6781   PetscFunctionBeginHot;
6782   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6783   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6784   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6785   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6786   PetscCall(PetscSectionGetNumFields(section, &numFields));
6787   /* Get points */
6788   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6789   /* Get array */
6790   PetscCall(VecGetArray(v, &array));
6791   /* Get values */
6792   for (f = 0; f < numFields; ++f) {
6793     const PetscInt    **perms = NULL;
6794     const PetscScalar **flips = NULL;
6795     PetscBool           contains;
6796 
6797     if (!fieldActive[f]) {
6798       for (p = 0; p < numPoints * 2; p += 2) {
6799         PetscInt fdof;
6800         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6801         offset += fdof;
6802       }
6803       continue;
6804     }
6805     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6806     switch (mode) {
6807     case INSERT_VALUES:
6808       for (p = 0; p < numPoints; p++) {
6809         const PetscInt     point = points[2 * p];
6810         const PetscInt    *perm  = perms ? perms[p] : NULL;
6811         const PetscScalar *flip  = flips ? flips[p] : NULL;
6812         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6813         if (!contains) continue;
6814         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6815       }
6816       break;
6817     case INSERT_ALL_VALUES:
6818       for (p = 0; p < numPoints; p++) {
6819         const PetscInt     point = points[2 * p];
6820         const PetscInt    *perm  = perms ? perms[p] : NULL;
6821         const PetscScalar *flip  = flips ? flips[p] : NULL;
6822         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6823         if (!contains) continue;
6824         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6825       }
6826       break;
6827     case INSERT_BC_VALUES:
6828       for (p = 0; p < numPoints; p++) {
6829         const PetscInt     point = points[2 * p];
6830         const PetscInt    *perm  = perms ? perms[p] : NULL;
6831         const PetscScalar *flip  = flips ? flips[p] : NULL;
6832         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6833         if (!contains) continue;
6834         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6835       }
6836       break;
6837     case ADD_VALUES:
6838       for (p = 0; p < numPoints; p++) {
6839         const PetscInt     point = points[2 * p];
6840         const PetscInt    *perm  = perms ? perms[p] : NULL;
6841         const PetscScalar *flip  = flips ? flips[p] : NULL;
6842         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6843         if (!contains) continue;
6844         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6845       }
6846       break;
6847     case ADD_ALL_VALUES:
6848       for (p = 0; p < numPoints; p++) {
6849         const PetscInt     point = points[2 * p];
6850         const PetscInt    *perm  = perms ? perms[p] : NULL;
6851         const PetscScalar *flip  = flips ? flips[p] : NULL;
6852         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6853         if (!contains) continue;
6854         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6855       }
6856       break;
6857     default:
6858       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6859     }
6860     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6861   }
6862   /* Cleanup points */
6863   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6864   /* Cleanup array */
6865   PetscCall(VecRestoreArray(v, &array));
6866   PetscFunctionReturn(0);
6867 }
6868 
6869 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6870 {
6871   PetscMPIInt rank;
6872   PetscInt    i, j;
6873 
6874   PetscFunctionBegin;
6875   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6876   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6877   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6878   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6879   numCIndices = numCIndices ? numCIndices : numRIndices;
6880   if (!values) PetscFunctionReturn(0);
6881   for (i = 0; i < numRIndices; i++) {
6882     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6883     for (j = 0; j < numCIndices; j++) {
6884 #if defined(PETSC_USE_COMPLEX)
6885       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6886 #else
6887       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6888 #endif
6889     }
6890     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6891   }
6892   PetscFunctionReturn(0);
6893 }
6894 
6895 /*
6896   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6897 
6898   Input Parameters:
6899 + section - The section for this data layout
6900 . islocal - Is the section (and thus indices being requested) local or global?
6901 . point   - The point contributing dofs with these indices
6902 . off     - The global offset of this point
6903 . loff    - The local offset of each field
6904 . setBC   - The flag determining whether to include indices of boundary values
6905 . perm    - A permutation of the dofs on this point, or NULL
6906 - indperm - A permutation of the entire indices array, or NULL
6907 
6908   Output Parameter:
6909 . indices - Indices for dofs on this point
6910 
6911   Level: developer
6912 
6913   Note: The indices could be local or global, depending on the value of 'off'.
6914 */
6915 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6916 {
6917   PetscInt        dof;   /* The number of unknowns on this point */
6918   PetscInt        cdof;  /* The number of constraints on this point */
6919   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6920   PetscInt        cind = 0, k;
6921 
6922   PetscFunctionBegin;
6923   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6924   PetscCall(PetscSectionGetDof(section, point, &dof));
6925   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6926   if (!cdof || setBC) {
6927     for (k = 0; k < dof; ++k) {
6928       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6929       const PetscInt ind    = indperm ? indperm[preind] : preind;
6930 
6931       indices[ind] = off + k;
6932     }
6933   } else {
6934     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6935     for (k = 0; k < dof; ++k) {
6936       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6937       const PetscInt ind    = indperm ? indperm[preind] : preind;
6938 
6939       if ((cind < cdof) && (k == cdofs[cind])) {
6940         /* Insert check for returning constrained indices */
6941         indices[ind] = -(off + k + 1);
6942         ++cind;
6943       } else {
6944         indices[ind] = off + k - (islocal ? 0 : cind);
6945       }
6946     }
6947   }
6948   *loff += dof;
6949   PetscFunctionReturn(0);
6950 }
6951 
6952 /*
6953  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6954 
6955  Input Parameters:
6956 + section - a section (global or local)
6957 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6958 . point - point within section
6959 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6960 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6961 . setBC - identify constrained (boundary condition) points via involution.
6962 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6963 . permsoff - offset
6964 - indperm - index permutation
6965 
6966  Output Parameter:
6967 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6968 . indices - array to hold indices (as defined by section) of each dof associated with point
6969 
6970  Notes:
6971  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6972  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6973  in the local vector.
6974 
6975  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6976  significant).  It is invalid to call with a global section and setBC=true.
6977 
6978  Developer Note:
6979  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6980  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6981  offset could be obtained from the section instead of passing it explicitly as we do now.
6982 
6983  Example:
6984  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6985  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6986  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6987  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.
6988 
6989  Level: developer
6990 */
6991 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[])
6992 {
6993   PetscInt numFields, foff, f;
6994 
6995   PetscFunctionBegin;
6996   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6997   PetscCall(PetscSectionGetNumFields(section, &numFields));
6998   for (f = 0, foff = 0; f < numFields; ++f) {
6999     PetscInt        fdof, cfdof;
7000     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7001     PetscInt        cind = 0, b;
7002     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7003 
7004     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7005     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7006     if (!cfdof || setBC) {
7007       for (b = 0; b < fdof; ++b) {
7008         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7009         const PetscInt ind    = indperm ? indperm[preind] : preind;
7010 
7011         indices[ind] = off + foff + b;
7012       }
7013     } else {
7014       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7015       for (b = 0; b < fdof; ++b) {
7016         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7017         const PetscInt ind    = indperm ? indperm[preind] : preind;
7018 
7019         if ((cind < cfdof) && (b == fcdofs[cind])) {
7020           indices[ind] = -(off + foff + b + 1);
7021           ++cind;
7022         } else {
7023           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7024         }
7025       }
7026     }
7027     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7028     foffs[f] += fdof;
7029   }
7030   PetscFunctionReturn(0);
7031 }
7032 
7033 /*
7034   This version believes the globalSection offsets for each field, rather than just the point offset
7035 
7036  . foffs - The offset into 'indices' for each field, since it is segregated by field
7037 
7038  Notes:
7039  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7040  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7041 */
7042 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7043 {
7044   PetscInt numFields, foff, f;
7045 
7046   PetscFunctionBegin;
7047   PetscCall(PetscSectionGetNumFields(section, &numFields));
7048   for (f = 0; f < numFields; ++f) {
7049     PetscInt        fdof, cfdof;
7050     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7051     PetscInt        cind = 0, b;
7052     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7053 
7054     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7055     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7056     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7057     if (!cfdof) {
7058       for (b = 0; b < fdof; ++b) {
7059         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7060         const PetscInt ind    = indperm ? indperm[preind] : preind;
7061 
7062         indices[ind] = foff + b;
7063       }
7064     } else {
7065       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7066       for (b = 0; b < fdof; ++b) {
7067         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7068         const PetscInt ind    = indperm ? indperm[preind] : preind;
7069 
7070         if ((cind < cfdof) && (b == fcdofs[cind])) {
7071           indices[ind] = -(foff + b + 1);
7072           ++cind;
7073         } else {
7074           indices[ind] = foff + b - cind;
7075         }
7076       }
7077     }
7078     foffs[f] += fdof;
7079   }
7080   PetscFunctionReturn(0);
7081 }
7082 
7083 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)
7084 {
7085   Mat             cMat;
7086   PetscSection    aSec, cSec;
7087   IS              aIS;
7088   PetscInt        aStart = -1, aEnd = -1;
7089   const PetscInt *anchors;
7090   PetscInt        numFields, f, p, q, newP = 0;
7091   PetscInt        newNumPoints = 0, newNumIndices = 0;
7092   PetscInt       *newPoints, *indices, *newIndices;
7093   PetscInt        maxAnchor, maxDof;
7094   PetscInt        newOffsets[32];
7095   PetscInt       *pointMatOffsets[32];
7096   PetscInt       *newPointOffsets[32];
7097   PetscScalar    *pointMat[32];
7098   PetscScalar    *newValues      = NULL, *tmpValues;
7099   PetscBool       anyConstrained = PETSC_FALSE;
7100 
7101   PetscFunctionBegin;
7102   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7103   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7104   PetscCall(PetscSectionGetNumFields(section, &numFields));
7105 
7106   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7107   /* if there are point-to-point constraints */
7108   if (aSec) {
7109     PetscCall(PetscArrayzero(newOffsets, 32));
7110     PetscCall(ISGetIndices(aIS, &anchors));
7111     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7112     /* figure out how many points are going to be in the new element matrix
7113      * (we allow double counting, because it's all just going to be summed
7114      * into the global matrix anyway) */
7115     for (p = 0; p < 2 * numPoints; p += 2) {
7116       PetscInt b    = points[p];
7117       PetscInt bDof = 0, bSecDof;
7118 
7119       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7120       if (!bSecDof) continue;
7121       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7122       if (bDof) {
7123         /* this point is constrained */
7124         /* it is going to be replaced by its anchors */
7125         PetscInt bOff, q;
7126 
7127         anyConstrained = PETSC_TRUE;
7128         newNumPoints += bDof;
7129         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7130         for (q = 0; q < bDof; q++) {
7131           PetscInt a = anchors[bOff + q];
7132           PetscInt aDof;
7133 
7134           PetscCall(PetscSectionGetDof(section, a, &aDof));
7135           newNumIndices += aDof;
7136           for (f = 0; f < numFields; ++f) {
7137             PetscInt fDof;
7138 
7139             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7140             newOffsets[f + 1] += fDof;
7141           }
7142         }
7143       } else {
7144         /* this point is not constrained */
7145         newNumPoints++;
7146         newNumIndices += bSecDof;
7147         for (f = 0; f < numFields; ++f) {
7148           PetscInt fDof;
7149 
7150           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7151           newOffsets[f + 1] += fDof;
7152         }
7153       }
7154     }
7155   }
7156   if (!anyConstrained) {
7157     if (outNumPoints) *outNumPoints = 0;
7158     if (outNumIndices) *outNumIndices = 0;
7159     if (outPoints) *outPoints = NULL;
7160     if (outValues) *outValues = NULL;
7161     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7162     PetscFunctionReturn(0);
7163   }
7164 
7165   if (outNumPoints) *outNumPoints = newNumPoints;
7166   if (outNumIndices) *outNumIndices = newNumIndices;
7167 
7168   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7169 
7170   if (!outPoints && !outValues) {
7171     if (offsets) {
7172       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7173     }
7174     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7175     PetscFunctionReturn(0);
7176   }
7177 
7178   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7179 
7180   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7181 
7182   /* workspaces */
7183   if (numFields) {
7184     for (f = 0; f < numFields; f++) {
7185       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7186       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7187     }
7188   } else {
7189     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7190     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7191   }
7192 
7193   /* get workspaces for the point-to-point matrices */
7194   if (numFields) {
7195     PetscInt totalOffset, totalMatOffset;
7196 
7197     for (p = 0; p < numPoints; p++) {
7198       PetscInt b    = points[2 * p];
7199       PetscInt bDof = 0, bSecDof;
7200 
7201       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7202       if (!bSecDof) {
7203         for (f = 0; f < numFields; f++) {
7204           newPointOffsets[f][p + 1] = 0;
7205           pointMatOffsets[f][p + 1] = 0;
7206         }
7207         continue;
7208       }
7209       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7210       if (bDof) {
7211         for (f = 0; f < numFields; f++) {
7212           PetscInt fDof, q, bOff, allFDof = 0;
7213 
7214           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7215           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7216           for (q = 0; q < bDof; q++) {
7217             PetscInt a = anchors[bOff + q];
7218             PetscInt aFDof;
7219 
7220             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7221             allFDof += aFDof;
7222           }
7223           newPointOffsets[f][p + 1] = allFDof;
7224           pointMatOffsets[f][p + 1] = fDof * allFDof;
7225         }
7226       } else {
7227         for (f = 0; f < numFields; f++) {
7228           PetscInt fDof;
7229 
7230           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7231           newPointOffsets[f][p + 1] = fDof;
7232           pointMatOffsets[f][p + 1] = 0;
7233         }
7234       }
7235     }
7236     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7237       newPointOffsets[f][0] = totalOffset;
7238       pointMatOffsets[f][0] = totalMatOffset;
7239       for (p = 0; p < numPoints; p++) {
7240         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7241         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7242       }
7243       totalOffset    = newPointOffsets[f][numPoints];
7244       totalMatOffset = pointMatOffsets[f][numPoints];
7245       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7246     }
7247   } else {
7248     for (p = 0; p < numPoints; p++) {
7249       PetscInt b    = points[2 * p];
7250       PetscInt bDof = 0, bSecDof;
7251 
7252       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7253       if (!bSecDof) {
7254         newPointOffsets[0][p + 1] = 0;
7255         pointMatOffsets[0][p + 1] = 0;
7256         continue;
7257       }
7258       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7259       if (bDof) {
7260         PetscInt bOff, q, allDof = 0;
7261 
7262         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7263         for (q = 0; q < bDof; q++) {
7264           PetscInt a = anchors[bOff + q], aDof;
7265 
7266           PetscCall(PetscSectionGetDof(section, a, &aDof));
7267           allDof += aDof;
7268         }
7269         newPointOffsets[0][p + 1] = allDof;
7270         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7271       } else {
7272         newPointOffsets[0][p + 1] = bSecDof;
7273         pointMatOffsets[0][p + 1] = 0;
7274       }
7275     }
7276     newPointOffsets[0][0] = 0;
7277     pointMatOffsets[0][0] = 0;
7278     for (p = 0; p < numPoints; p++) {
7279       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7280       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7281     }
7282     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7283   }
7284 
7285   /* output arrays */
7286   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7287 
7288   /* get the point-to-point matrices; construct newPoints */
7289   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7290   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7291   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7292   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7293   if (numFields) {
7294     for (p = 0, newP = 0; p < numPoints; p++) {
7295       PetscInt b    = points[2 * p];
7296       PetscInt o    = points[2 * p + 1];
7297       PetscInt bDof = 0, bSecDof;
7298 
7299       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7300       if (!bSecDof) continue;
7301       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7302       if (bDof) {
7303         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7304 
7305         fStart[0] = 0;
7306         fEnd[0]   = 0;
7307         for (f = 0; f < numFields; f++) {
7308           PetscInt fDof;
7309 
7310           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7311           fStart[f + 1] = fStart[f] + fDof;
7312           fEnd[f + 1]   = fStart[f + 1];
7313         }
7314         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7315         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7316 
7317         fAnchorStart[0] = 0;
7318         fAnchorEnd[0]   = 0;
7319         for (f = 0; f < numFields; f++) {
7320           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7321 
7322           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7323           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7324         }
7325         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7326         for (q = 0; q < bDof; q++) {
7327           PetscInt a = anchors[bOff + q], aOff;
7328 
7329           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7330           newPoints[2 * (newP + q)]     = a;
7331           newPoints[2 * (newP + q) + 1] = 0;
7332           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7333           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7334         }
7335         newP += bDof;
7336 
7337         if (outValues) {
7338           /* get the point-to-point submatrix */
7339           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]));
7340         }
7341       } else {
7342         newPoints[2 * newP]     = b;
7343         newPoints[2 * newP + 1] = o;
7344         newP++;
7345       }
7346     }
7347   } else {
7348     for (p = 0; p < numPoints; p++) {
7349       PetscInt b    = points[2 * p];
7350       PetscInt o    = points[2 * p + 1];
7351       PetscInt bDof = 0, bSecDof;
7352 
7353       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7354       if (!bSecDof) continue;
7355       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7356       if (bDof) {
7357         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7358 
7359         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7360         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7361 
7362         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7363         for (q = 0; q < bDof; q++) {
7364           PetscInt a = anchors[bOff + q], aOff;
7365 
7366           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7367 
7368           newPoints[2 * (newP + q)]     = a;
7369           newPoints[2 * (newP + q) + 1] = 0;
7370           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7371           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7372         }
7373         newP += bDof;
7374 
7375         /* get the point-to-point submatrix */
7376         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7377       } else {
7378         newPoints[2 * newP]     = b;
7379         newPoints[2 * newP + 1] = o;
7380         newP++;
7381       }
7382     }
7383   }
7384 
7385   if (outValues) {
7386     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7387     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7388     /* multiply constraints on the right */
7389     if (numFields) {
7390       for (f = 0; f < numFields; f++) {
7391         PetscInt oldOff = offsets[f];
7392 
7393         for (p = 0; p < numPoints; p++) {
7394           PetscInt cStart = newPointOffsets[f][p];
7395           PetscInt b      = points[2 * p];
7396           PetscInt c, r, k;
7397           PetscInt dof;
7398 
7399           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7400           if (!dof) continue;
7401           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7402             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7403             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7404 
7405             for (r = 0; r < numIndices; r++) {
7406               for (c = 0; c < nCols; c++) {
7407                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7408               }
7409             }
7410           } else {
7411             /* copy this column as is */
7412             for (r = 0; r < numIndices; r++) {
7413               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7414             }
7415           }
7416           oldOff += dof;
7417         }
7418       }
7419     } else {
7420       PetscInt oldOff = 0;
7421       for (p = 0; p < numPoints; p++) {
7422         PetscInt cStart = newPointOffsets[0][p];
7423         PetscInt b      = points[2 * p];
7424         PetscInt c, r, k;
7425         PetscInt dof;
7426 
7427         PetscCall(PetscSectionGetDof(section, b, &dof));
7428         if (!dof) continue;
7429         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7430           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7431           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7432 
7433           for (r = 0; r < numIndices; r++) {
7434             for (c = 0; c < nCols; c++) {
7435               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7436             }
7437           }
7438         } else {
7439           /* copy this column as is */
7440           for (r = 0; r < numIndices; r++) {
7441             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7442           }
7443         }
7444         oldOff += dof;
7445       }
7446     }
7447 
7448     if (multiplyLeft) {
7449       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7450       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7451       /* multiply constraints transpose on the left */
7452       if (numFields) {
7453         for (f = 0; f < numFields; f++) {
7454           PetscInt oldOff = offsets[f];
7455 
7456           for (p = 0; p < numPoints; p++) {
7457             PetscInt rStart = newPointOffsets[f][p];
7458             PetscInt b      = points[2 * p];
7459             PetscInt c, r, k;
7460             PetscInt dof;
7461 
7462             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7463             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7464               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7465               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7466 
7467               for (r = 0; r < nRows; r++) {
7468                 for (c = 0; c < newNumIndices; c++) {
7469                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7470                 }
7471               }
7472             } else {
7473               /* copy this row as is */
7474               for (r = 0; r < dof; r++) {
7475                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7476               }
7477             }
7478             oldOff += dof;
7479           }
7480         }
7481       } else {
7482         PetscInt oldOff = 0;
7483 
7484         for (p = 0; p < numPoints; p++) {
7485           PetscInt rStart = newPointOffsets[0][p];
7486           PetscInt b      = points[2 * p];
7487           PetscInt c, r, k;
7488           PetscInt dof;
7489 
7490           PetscCall(PetscSectionGetDof(section, b, &dof));
7491           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7492             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7493             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7494 
7495             for (r = 0; r < nRows; r++) {
7496               for (c = 0; c < newNumIndices; c++) {
7497                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7498               }
7499             }
7500           } else {
7501             /* copy this row as is */
7502             for (r = 0; r < dof; r++) {
7503               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7504             }
7505           }
7506           oldOff += dof;
7507         }
7508       }
7509 
7510       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7511     } else {
7512       newValues = tmpValues;
7513     }
7514   }
7515 
7516   /* clean up */
7517   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7518   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7519 
7520   if (numFields) {
7521     for (f = 0; f < numFields; f++) {
7522       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7523       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7524       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7525     }
7526   } else {
7527     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7528     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7529     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7530   }
7531   PetscCall(ISRestoreIndices(aIS, &anchors));
7532 
7533   /* output */
7534   if (outPoints) {
7535     *outPoints = newPoints;
7536   } else {
7537     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7538   }
7539   if (outValues) *outValues = newValues;
7540   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7541   PetscFunctionReturn(0);
7542 }
7543 
7544 /*@C
7545   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7546 
7547   Not collective
7548 
7549   Input Parameters:
7550 + dm         - The `DM`
7551 . section    - The `PetscSection` describing the points (a local section)
7552 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7553 . point      - The point defining the closure
7554 - useClPerm  - Use the closure point permutation if available
7555 
7556   Output Parameters:
7557 + numIndices - The number of dof indices in the closure of point with the input sections
7558 . indices    - The dof indices
7559 . outOffsets - Array to write the field offsets into, or NULL
7560 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7561 
7562   Level: advanced
7563 
7564   Notes:
7565   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7566 
7567   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7568   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7569   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7570   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7571   indices (with the above semantics) are implied.
7572 
7573 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7574           `PetscSection`, `DMGetGlobalSection()`
7575 @*/
7576 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7577 {
7578   /* Closure ordering */
7579   PetscSection    clSection;
7580   IS              clPoints;
7581   const PetscInt *clp;
7582   PetscInt       *points;
7583   const PetscInt *clperm = NULL;
7584   /* Dof permutation and sign flips */
7585   const PetscInt    **perms[32] = {NULL};
7586   const PetscScalar **flips[32] = {NULL};
7587   PetscScalar        *valCopy   = NULL;
7588   /* Hanging node constraints */
7589   PetscInt    *pointsC = NULL;
7590   PetscScalar *valuesC = NULL;
7591   PetscInt     NclC, NiC;
7592 
7593   PetscInt *idx;
7594   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7595   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7596 
7597   PetscFunctionBeginHot;
7598   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7599   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7600   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7601   if (numIndices) PetscValidIntPointer(numIndices, 6);
7602   if (indices) PetscValidPointer(indices, 7);
7603   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7604   if (values) PetscValidPointer(values, 9);
7605   PetscCall(PetscSectionGetNumFields(section, &Nf));
7606   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7607   PetscCall(PetscArrayzero(offsets, 32));
7608   /* 1) Get points in closure */
7609   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7610   if (useClPerm) {
7611     PetscInt depth, clsize;
7612     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7613     for (clsize = 0, p = 0; p < Ncl; p++) {
7614       PetscInt dof;
7615       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7616       clsize += dof;
7617     }
7618     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7619   }
7620   /* 2) Get number of indices on these points and field offsets from section */
7621   for (p = 0; p < Ncl * 2; p += 2) {
7622     PetscInt dof, fdof;
7623 
7624     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7625     for (f = 0; f < Nf; ++f) {
7626       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7627       offsets[f + 1] += fdof;
7628     }
7629     Ni += dof;
7630   }
7631   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7632   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7633   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7634   for (f = 0; f < PetscMax(1, Nf); ++f) {
7635     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7636     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7637     /* may need to apply sign changes to the element matrix */
7638     if (values && flips[f]) {
7639       PetscInt foffset = offsets[f];
7640 
7641       for (p = 0; p < Ncl; ++p) {
7642         PetscInt           pnt  = points[2 * p], fdof;
7643         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7644 
7645         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7646         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7647         if (flip) {
7648           PetscInt i, j, k;
7649 
7650           if (!valCopy) {
7651             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7652             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7653             *values = valCopy;
7654           }
7655           for (i = 0; i < fdof; ++i) {
7656             PetscScalar fval = flip[i];
7657 
7658             for (k = 0; k < Ni; ++k) {
7659               valCopy[Ni * (foffset + i) + k] *= fval;
7660               valCopy[Ni * k + (foffset + i)] *= fval;
7661             }
7662           }
7663         }
7664         foffset += fdof;
7665       }
7666     }
7667   }
7668   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7669   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7670   if (NclC) {
7671     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7672     for (f = 0; f < PetscMax(1, Nf); ++f) {
7673       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7674       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7675     }
7676     for (f = 0; f < PetscMax(1, Nf); ++f) {
7677       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7678       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7679     }
7680     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7681     Ncl    = NclC;
7682     Ni     = NiC;
7683     points = pointsC;
7684     if (values) *values = valuesC;
7685   }
7686   /* 5) Calculate indices */
7687   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7688   if (Nf) {
7689     PetscInt  idxOff;
7690     PetscBool useFieldOffsets;
7691 
7692     if (outOffsets) {
7693       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7694     }
7695     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7696     if (useFieldOffsets) {
7697       for (p = 0; p < Ncl; ++p) {
7698         const PetscInt pnt = points[p * 2];
7699 
7700         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7701       }
7702     } else {
7703       for (p = 0; p < Ncl; ++p) {
7704         const PetscInt pnt = points[p * 2];
7705 
7706         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7707         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7708          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7709          * global section. */
7710         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7711       }
7712     }
7713   } else {
7714     PetscInt off = 0, idxOff;
7715 
7716     for (p = 0; p < Ncl; ++p) {
7717       const PetscInt  pnt  = points[p * 2];
7718       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7719 
7720       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7721       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7722        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7723       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7724     }
7725   }
7726   /* 6) Cleanup */
7727   for (f = 0; f < PetscMax(1, Nf); ++f) {
7728     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7729     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7730   }
7731   if (NclC) {
7732     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7733   } else {
7734     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7735   }
7736 
7737   if (numIndices) *numIndices = Ni;
7738   if (indices) *indices = idx;
7739   PetscFunctionReturn(0);
7740 }
7741 
7742 /*@C
7743   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7744 
7745   Not collective
7746 
7747   Input Parameters:
7748 + dm         - The `DM`
7749 . section    - The `PetscSection` describing the points (a local section)
7750 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7751 . point      - The point defining the closure
7752 - useClPerm  - Use the closure point permutation if available
7753 
7754   Output Parameters:
7755 + numIndices - The number of dof indices in the closure of point with the input sections
7756 . indices    - The dof indices
7757 . outOffsets - Array to write the field offsets into, or NULL
7758 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7759 
7760   Level: advanced
7761 
7762   Notes:
7763   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7764 
7765   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7766   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7767   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7768   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7769   indices (with the above semantics) are implied.
7770 
7771 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7772 @*/
7773 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7774 {
7775   PetscFunctionBegin;
7776   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7777   PetscValidPointer(indices, 7);
7778   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7779   PetscFunctionReturn(0);
7780 }
7781 
7782 /*@C
7783   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7784 
7785   Not collective
7786 
7787   Input Parameters:
7788 + dm - The `DM`
7789 . section - The section describing the layout in v, or NULL to use the default section
7790 . globalSection - The section describing the layout in v, or NULL to use the default global section
7791 . A - The matrix
7792 . point - The point in the `DM`
7793 . values - The array of values
7794 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7795 
7796   Level: intermediate
7797 
7798 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7799 @*/
7800 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7801 {
7802   DM_Plex           *mesh = (DM_Plex *)dm->data;
7803   PetscInt          *indices;
7804   PetscInt           numIndices;
7805   const PetscScalar *valuesOrig = values;
7806   PetscErrorCode     ierr;
7807 
7808   PetscFunctionBegin;
7809   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7810   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7811   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7812   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7813   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7814   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7815 
7816   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7817 
7818   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7819   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7820   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7821   if (ierr) {
7822     PetscMPIInt rank;
7823 
7824     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7825     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7826     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7827     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7828     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7829     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7830   }
7831   if (mesh->printFEM > 1) {
7832     PetscInt i;
7833     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7834     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7835     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7836   }
7837 
7838   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7839   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7840   PetscFunctionReturn(0);
7841 }
7842 
7843 /*@C
7844   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7845 
7846   Not collective
7847 
7848   Input Parameters:
7849 + dmRow - The `DM` for the row fields
7850 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7851 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7852 . dmCol - The `DM` for the column fields
7853 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7854 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7855 . A - The matrix
7856 . point - The point in the `DM`
7857 . values - The array of values
7858 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7859 
7860   Level: intermediate
7861 
7862 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7863 @*/
7864 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7865 {
7866   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7867   PetscInt          *indicesRow, *indicesCol;
7868   PetscInt           numIndicesRow, numIndicesCol;
7869   const PetscScalar *valuesOrig = values;
7870   PetscErrorCode     ierr;
7871 
7872   PetscFunctionBegin;
7873   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7874   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7875   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7876   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7877   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7878   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7879   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7880   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7881   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7882   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7883   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7884 
7885   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7886   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7887 
7888   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7889   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7890   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7891   if (ierr) {
7892     PetscMPIInt rank;
7893 
7894     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7895     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7896     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7897     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7898     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7899     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7900   }
7901 
7902   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7903   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7904   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7905   PetscFunctionReturn(0);
7906 }
7907 
7908 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7909 {
7910   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7911   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7912   PetscInt       *cpoints = NULL;
7913   PetscInt       *findices, *cindices;
7914   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7915   PetscInt        foffsets[32], coffsets[32];
7916   DMPolytopeType  ct;
7917   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7918   PetscErrorCode  ierr;
7919 
7920   PetscFunctionBegin;
7921   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7922   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7923   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7924   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7925   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7926   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7927   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7928   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7929   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7930   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7931   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7932   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7933   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7934   PetscCall(PetscArrayzero(foffsets, 32));
7935   PetscCall(PetscArrayzero(coffsets, 32));
7936   /* Column indices */
7937   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7938   maxFPoints = numCPoints;
7939   /* Compress out points not in the section */
7940   /*   TODO: Squeeze out points with 0 dof as well */
7941   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7942   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7943     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7944       cpoints[q * 2]     = cpoints[p];
7945       cpoints[q * 2 + 1] = cpoints[p + 1];
7946       ++q;
7947     }
7948   }
7949   numCPoints = q;
7950   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7951     PetscInt fdof;
7952 
7953     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7954     if (!dof) continue;
7955     for (f = 0; f < numFields; ++f) {
7956       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7957       coffsets[f + 1] += fdof;
7958     }
7959     numCIndices += dof;
7960   }
7961   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7962   /* Row indices */
7963   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7964   {
7965     DMPlexTransform tr;
7966     DMPolytopeType *rct;
7967     PetscInt       *rsize, *rcone, *rornt, Nt;
7968 
7969     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7970     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7971     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7972     numSubcells = rsize[Nt - 1];
7973     PetscCall(DMPlexTransformDestroy(&tr));
7974   }
7975   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7976   for (r = 0, q = 0; r < numSubcells; ++r) {
7977     /* TODO Map from coarse to fine cells */
7978     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7979     /* Compress out points not in the section */
7980     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7981     for (p = 0; p < numFPoints * 2; p += 2) {
7982       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7983         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7984         if (!dof) continue;
7985         for (s = 0; s < q; ++s)
7986           if (fpoints[p] == ftotpoints[s * 2]) break;
7987         if (s < q) continue;
7988         ftotpoints[q * 2]     = fpoints[p];
7989         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7990         ++q;
7991       }
7992     }
7993     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7994   }
7995   numFPoints = q;
7996   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7997     PetscInt fdof;
7998 
7999     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8000     if (!dof) continue;
8001     for (f = 0; f < numFields; ++f) {
8002       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8003       foffsets[f + 1] += fdof;
8004     }
8005     numFIndices += dof;
8006   }
8007   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8008 
8009   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8010   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8011   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8012   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8013   if (numFields) {
8014     const PetscInt **permsF[32] = {NULL};
8015     const PetscInt **permsC[32] = {NULL};
8016 
8017     for (f = 0; f < numFields; f++) {
8018       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8019       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8020     }
8021     for (p = 0; p < numFPoints; p++) {
8022       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8023       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8024     }
8025     for (p = 0; p < numCPoints; p++) {
8026       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8027       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8028     }
8029     for (f = 0; f < numFields; f++) {
8030       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8031       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8032     }
8033   } else {
8034     const PetscInt **permsF = NULL;
8035     const PetscInt **permsC = NULL;
8036 
8037     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8038     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8039     for (p = 0, off = 0; p < numFPoints; p++) {
8040       const PetscInt *perm = permsF ? permsF[p] : NULL;
8041 
8042       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8043       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8044     }
8045     for (p = 0, off = 0; p < numCPoints; p++) {
8046       const PetscInt *perm = permsC ? permsC[p] : NULL;
8047 
8048       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8049       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8050     }
8051     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8052     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8053   }
8054   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8055   /* TODO: flips */
8056   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8057   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8058   if (ierr) {
8059     PetscMPIInt rank;
8060 
8061     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8062     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8063     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8064     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8065     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8066   }
8067   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8068   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8069   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8070   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8071   PetscFunctionReturn(0);
8072 }
8073 
8074 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8075 {
8076   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8077   PetscInt       *cpoints = NULL;
8078   PetscInt        foffsets[32], coffsets[32];
8079   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8080   DMPolytopeType  ct;
8081   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8082 
8083   PetscFunctionBegin;
8084   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8085   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8086   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8087   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8088   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8089   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8090   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8091   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8092   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8093   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8094   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8095   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8096   PetscCall(PetscArrayzero(foffsets, 32));
8097   PetscCall(PetscArrayzero(coffsets, 32));
8098   /* Column indices */
8099   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8100   maxFPoints = numCPoints;
8101   /* Compress out points not in the section */
8102   /*   TODO: Squeeze out points with 0 dof as well */
8103   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8104   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8105     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8106       cpoints[q * 2]     = cpoints[p];
8107       cpoints[q * 2 + 1] = cpoints[p + 1];
8108       ++q;
8109     }
8110   }
8111   numCPoints = q;
8112   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8113     PetscInt fdof;
8114 
8115     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8116     if (!dof) continue;
8117     for (f = 0; f < numFields; ++f) {
8118       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8119       coffsets[f + 1] += fdof;
8120     }
8121     numCIndices += dof;
8122   }
8123   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8124   /* Row indices */
8125   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8126   {
8127     DMPlexTransform tr;
8128     DMPolytopeType *rct;
8129     PetscInt       *rsize, *rcone, *rornt, Nt;
8130 
8131     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8132     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8133     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8134     numSubcells = rsize[Nt - 1];
8135     PetscCall(DMPlexTransformDestroy(&tr));
8136   }
8137   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8138   for (r = 0, q = 0; r < numSubcells; ++r) {
8139     /* TODO Map from coarse to fine cells */
8140     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8141     /* Compress out points not in the section */
8142     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8143     for (p = 0; p < numFPoints * 2; p += 2) {
8144       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8145         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8146         if (!dof) continue;
8147         for (s = 0; s < q; ++s)
8148           if (fpoints[p] == ftotpoints[s * 2]) break;
8149         if (s < q) continue;
8150         ftotpoints[q * 2]     = fpoints[p];
8151         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8152         ++q;
8153       }
8154     }
8155     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8156   }
8157   numFPoints = q;
8158   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8159     PetscInt fdof;
8160 
8161     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8162     if (!dof) continue;
8163     for (f = 0; f < numFields; ++f) {
8164       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8165       foffsets[f + 1] += fdof;
8166     }
8167     numFIndices += dof;
8168   }
8169   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8170 
8171   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8172   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8173   if (numFields) {
8174     const PetscInt **permsF[32] = {NULL};
8175     const PetscInt **permsC[32] = {NULL};
8176 
8177     for (f = 0; f < numFields; f++) {
8178       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8179       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8180     }
8181     for (p = 0; p < numFPoints; p++) {
8182       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8183       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8184     }
8185     for (p = 0; p < numCPoints; p++) {
8186       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8187       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8188     }
8189     for (f = 0; f < numFields; f++) {
8190       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8191       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8192     }
8193   } else {
8194     const PetscInt **permsF = NULL;
8195     const PetscInt **permsC = NULL;
8196 
8197     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8198     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8199     for (p = 0, off = 0; p < numFPoints; p++) {
8200       const PetscInt *perm = permsF ? permsF[p] : NULL;
8201 
8202       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8203       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8204     }
8205     for (p = 0, off = 0; p < numCPoints; p++) {
8206       const PetscInt *perm = permsC ? permsC[p] : NULL;
8207 
8208       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8209       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8210     }
8211     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8212     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8213   }
8214   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8215   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8216   PetscFunctionReturn(0);
8217 }
8218 
8219 /*@C
8220   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8221 
8222   Input Parameter:
8223 . dm   - The `DMPLEX` object
8224 
8225   Output Parameter:
8226 . cellHeight - The height of a cell
8227 
8228   Level: developer
8229 
8230 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`,  `DMPlexSetVTKCellHeight()`
8231 @*/
8232 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8233 {
8234   DM_Plex *mesh = (DM_Plex *)dm->data;
8235 
8236   PetscFunctionBegin;
8237   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8238   PetscValidIntPointer(cellHeight, 2);
8239   *cellHeight = mesh->vtkCellHeight;
8240   PetscFunctionReturn(0);
8241 }
8242 
8243 /*@C
8244   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8245 
8246   Input Parameters:
8247 + dm   - The `DMPLEX` object
8248 - cellHeight - The height of a cell
8249 
8250   Level: developer
8251 
8252 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8253 @*/
8254 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8255 {
8256   DM_Plex *mesh = (DM_Plex *)dm->data;
8257 
8258   PetscFunctionBegin;
8259   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8260   mesh->vtkCellHeight = cellHeight;
8261   PetscFunctionReturn(0);
8262 }
8263 
8264 /*@
8265   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8266 
8267   Input Parameter:
8268 . dm - The `DMPLEX` object
8269 
8270   Output Parameters:
8271 + gcStart - The first ghost cell, or NULL
8272 - gcEnd   - The upper bound on ghost cells, or NULL
8273 
8274   Level: advanced
8275 
8276 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8277 @*/
8278 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8279 {
8280   DMLabel ctLabel;
8281 
8282   PetscFunctionBegin;
8283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8284   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8285   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8286   // Reset label for fast lookup
8287   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8288   PetscFunctionReturn(0);
8289 }
8290 
8291 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8292 {
8293   PetscSection section, globalSection;
8294   PetscInt    *numbers, p;
8295 
8296   PetscFunctionBegin;
8297   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8298   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8299   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8300   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8301   PetscCall(PetscSectionSetUp(section));
8302   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8303   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8304   for (p = pStart; p < pEnd; ++p) {
8305     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8306     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8307     else numbers[p - pStart] += shift;
8308   }
8309   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8310   if (globalSize) {
8311     PetscLayout layout;
8312     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8313     PetscCall(PetscLayoutGetSize(layout, globalSize));
8314     PetscCall(PetscLayoutDestroy(&layout));
8315   }
8316   PetscCall(PetscSectionDestroy(&section));
8317   PetscCall(PetscSectionDestroy(&globalSection));
8318   PetscFunctionReturn(0);
8319 }
8320 
8321 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8322 {
8323   PetscInt cellHeight, cStart, cEnd;
8324 
8325   PetscFunctionBegin;
8326   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8327   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8328   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8329   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8330   PetscFunctionReturn(0);
8331 }
8332 
8333 /*@
8334   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8335 
8336   Input Parameter:
8337 . dm   - The `DMPLEX` object
8338 
8339   Output Parameter:
8340 . globalCellNumbers - Global cell numbers for all cells on this process
8341 
8342   Level: developer
8343 
8344 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8345 @*/
8346 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8347 {
8348   DM_Plex *mesh = (DM_Plex *)dm->data;
8349 
8350   PetscFunctionBegin;
8351   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8352   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8353   *globalCellNumbers = mesh->globalCellNumbers;
8354   PetscFunctionReturn(0);
8355 }
8356 
8357 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8358 {
8359   PetscInt vStart, vEnd;
8360 
8361   PetscFunctionBegin;
8362   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8363   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8364   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8365   PetscFunctionReturn(0);
8366 }
8367 
8368 /*@
8369   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8370 
8371   Input Parameter:
8372 . dm   - The `DMPLEX` object
8373 
8374   Output Parameter:
8375 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8376 
8377   Level: developer
8378 
8379 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8380 @*/
8381 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8382 {
8383   DM_Plex *mesh = (DM_Plex *)dm->data;
8384 
8385   PetscFunctionBegin;
8386   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8387   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8388   *globalVertexNumbers = mesh->globalVertexNumbers;
8389   PetscFunctionReturn(0);
8390 }
8391 
8392 /*@
8393   DMPlexCreatePointNumbering - Create a global numbering for all points.
8394 
8395   Collective on dm
8396 
8397   Input Parameter:
8398 . dm   - The `DMPLEX` object
8399 
8400   Output Parameter:
8401 . globalPointNumbers - Global numbers for all points on this process
8402 
8403   Level: developer
8404 
8405   Notes:
8406   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8407   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8408   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8409   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8410 
8411   The partitioned mesh is
8412 ```
8413  (2)--0--(3)--1--(4)    (1)--0--(2)
8414 ```
8415   and its global numbering is
8416 ```
8417   (3)--0--(4)--1--(5)--2--(6)
8418 ```
8419   Then the global numbering is provided as
8420 ```
8421 [0] Number of indices in set 5
8422 [0] 0 0
8423 [0] 1 1
8424 [0] 2 3
8425 [0] 3 4
8426 [0] 4 -6
8427 [1] Number of indices in set 3
8428 [1] 0 2
8429 [1] 1 5
8430 [1] 2 6
8431 ```
8432 
8433 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8434 @*/
8435 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8436 {
8437   IS        nums[4];
8438   PetscInt  depths[4], gdepths[4], starts[4];
8439   PetscInt  depth, d, shift = 0;
8440   PetscBool empty = PETSC_FALSE;
8441 
8442   PetscFunctionBegin;
8443   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8444   PetscCall(DMPlexGetDepth(dm, &depth));
8445   // For unstratified meshes use dim instead of depth
8446   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8447   // If any stratum is empty, we must mark all empty
8448   for (d = 0; d <= depth; ++d) {
8449     PetscInt end;
8450 
8451     depths[d] = depth - d;
8452     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8453     if (!(starts[d] - end)) empty = PETSC_TRUE;
8454   }
8455   if (empty)
8456     for (d = 0; d <= depth; ++d) {
8457       depths[d] = -1;
8458       starts[d] = -1;
8459     }
8460   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8461   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8462   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]);
8463   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8464   for (d = 0; d <= depth; ++d) {
8465     PetscInt pStart, pEnd, gsize;
8466 
8467     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8468     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8469     shift += gsize;
8470   }
8471   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8472   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8473   PetscFunctionReturn(0);
8474 }
8475 
8476 /*@
8477   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8478 
8479   Input Parameter:
8480 . dm - The `DMPLEX` object
8481 
8482   Output Parameter:
8483 . ranks - The rank field
8484 
8485   Options Database Key:
8486 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8487 
8488   Level: intermediate
8489 
8490 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8491 @*/
8492 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8493 {
8494   DM             rdm;
8495   PetscFE        fe;
8496   PetscScalar   *r;
8497   PetscMPIInt    rank;
8498   DMPolytopeType ct;
8499   PetscInt       dim, cStart, cEnd, c;
8500   PetscBool      simplex;
8501 
8502   PetscFunctionBeginUser;
8503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8504   PetscValidPointer(ranks, 2);
8505   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8506   PetscCall(DMClone(dm, &rdm));
8507   PetscCall(DMGetDimension(rdm, &dim));
8508   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8509   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8510   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8511   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8512   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8513   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8514   PetscCall(PetscFEDestroy(&fe));
8515   PetscCall(DMCreateDS(rdm));
8516   PetscCall(DMCreateGlobalVector(rdm, ranks));
8517   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8518   PetscCall(VecGetArray(*ranks, &r));
8519   for (c = cStart; c < cEnd; ++c) {
8520     PetscScalar *lr;
8521 
8522     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8523     if (lr) *lr = rank;
8524   }
8525   PetscCall(VecRestoreArray(*ranks, &r));
8526   PetscCall(DMDestroy(&rdm));
8527   PetscFunctionReturn(0);
8528 }
8529 
8530 /*@
8531   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8532 
8533   Input Parameters:
8534 + dm    - The DMPlex
8535 - label - The DMLabel
8536 
8537   Output Parameter:
8538 . val - The label value field
8539 
8540   Options Database Keys:
8541 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8542 
8543   Level: intermediate
8544 
8545 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8546 @*/
8547 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8548 {
8549   DM           rdm;
8550   PetscFE      fe;
8551   PetscScalar *v;
8552   PetscInt     dim, cStart, cEnd, c;
8553 
8554   PetscFunctionBeginUser;
8555   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8556   PetscValidPointer(label, 2);
8557   PetscValidPointer(val, 3);
8558   PetscCall(DMClone(dm, &rdm));
8559   PetscCall(DMGetDimension(rdm, &dim));
8560   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8561   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8562   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8563   PetscCall(PetscFEDestroy(&fe));
8564   PetscCall(DMCreateDS(rdm));
8565   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8566   PetscCall(DMCreateGlobalVector(rdm, val));
8567   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8568   PetscCall(VecGetArray(*val, &v));
8569   for (c = cStart; c < cEnd; ++c) {
8570     PetscScalar *lv;
8571     PetscInt     cval;
8572 
8573     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8574     PetscCall(DMLabelGetValue(label, c, &cval));
8575     *lv = cval;
8576   }
8577   PetscCall(VecRestoreArray(*val, &v));
8578   PetscCall(DMDestroy(&rdm));
8579   PetscFunctionReturn(0);
8580 }
8581 
8582 /*@
8583   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8584 
8585   Input Parameter:
8586 . dm - The `DMPLEX` object
8587 
8588   Level: developer
8589 
8590   Notes:
8591   This is a useful diagnostic when creating meshes programmatically.
8592 
8593   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8594 
8595 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8596 @*/
8597 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8598 {
8599   PetscSection    coneSection, supportSection;
8600   const PetscInt *cone, *support;
8601   PetscInt        coneSize, c, supportSize, s;
8602   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8603   PetscBool       storagecheck = PETSC_TRUE;
8604 
8605   PetscFunctionBegin;
8606   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8607   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8608   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8609   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8610   /* Check that point p is found in the support of its cone points, and vice versa */
8611   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8612   for (p = pStart; p < pEnd; ++p) {
8613     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8614     PetscCall(DMPlexGetCone(dm, p, &cone));
8615     for (c = 0; c < coneSize; ++c) {
8616       PetscBool dup = PETSC_FALSE;
8617       PetscInt  d;
8618       for (d = c - 1; d >= 0; --d) {
8619         if (cone[c] == cone[d]) {
8620           dup = PETSC_TRUE;
8621           break;
8622         }
8623       }
8624       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8625       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8626       for (s = 0; s < supportSize; ++s) {
8627         if (support[s] == p) break;
8628       }
8629       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8630         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8631         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8632         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8633         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8634         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8635         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8636         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]);
8637         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8638       }
8639     }
8640     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8641     if (p != pp) {
8642       storagecheck = PETSC_FALSE;
8643       continue;
8644     }
8645     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8646     PetscCall(DMPlexGetSupport(dm, p, &support));
8647     for (s = 0; s < supportSize; ++s) {
8648       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8649       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8650       for (c = 0; c < coneSize; ++c) {
8651         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8652         if (cone[c] != pp) {
8653           c = 0;
8654           break;
8655         }
8656         if (cone[c] == p) break;
8657       }
8658       if (c >= coneSize) {
8659         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8660         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8661         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8662         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8663         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8664         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8665         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8666       }
8667     }
8668   }
8669   if (storagecheck) {
8670     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8671     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8672     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8673   }
8674   PetscFunctionReturn(0);
8675 }
8676 
8677 /*
8678   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.
8679 */
8680 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8681 {
8682   DMPolytopeType  cct;
8683   PetscInt        ptpoints[4];
8684   const PetscInt *cone, *ccone, *ptcone;
8685   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8686 
8687   PetscFunctionBegin;
8688   *unsplit = 0;
8689   switch (ct) {
8690   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8691     ptpoints[npt++] = c;
8692     break;
8693   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8694     PetscCall(DMPlexGetCone(dm, c, &cone));
8695     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8696     for (cp = 0; cp < coneSize; ++cp) {
8697       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8698       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8699     }
8700     break;
8701   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8702   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8703     PetscCall(DMPlexGetCone(dm, c, &cone));
8704     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8705     for (cp = 0; cp < coneSize; ++cp) {
8706       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8707       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8708       for (ccp = 0; ccp < cconeSize; ++ccp) {
8709         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8710         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8711           PetscInt p;
8712           for (p = 0; p < npt; ++p)
8713             if (ptpoints[p] == ccone[ccp]) break;
8714           if (p == npt) ptpoints[npt++] = ccone[ccp];
8715         }
8716       }
8717     }
8718     break;
8719   default:
8720     break;
8721   }
8722   for (pt = 0; pt < npt; ++pt) {
8723     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8724     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8725   }
8726   PetscFunctionReturn(0);
8727 }
8728 
8729 /*@
8730   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8731 
8732   Input Parameters:
8733 + dm - The `DMPLEX` object
8734 - cellHeight - Normally 0
8735 
8736   Level: developer
8737 
8738   Notes:
8739   This is a useful diagnostic when creating meshes programmatically.
8740   Currently applicable only to homogeneous simplex or tensor meshes.
8741 
8742   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8743 
8744 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8745 @*/
8746 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8747 {
8748   DMPlexInterpolatedFlag interp;
8749   DMPolytopeType         ct;
8750   PetscInt               vStart, vEnd, cStart, cEnd, c;
8751 
8752   PetscFunctionBegin;
8753   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8754   PetscCall(DMPlexIsInterpolated(dm, &interp));
8755   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8756   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8757   for (c = cStart; c < cEnd; ++c) {
8758     PetscInt *closure = NULL;
8759     PetscInt  coneSize, closureSize, cl, Nv = 0;
8760 
8761     PetscCall(DMPlexGetCellType(dm, c, &ct));
8762     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8763     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8764     if (interp == DMPLEX_INTERPOLATED_FULL) {
8765       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8766       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));
8767     }
8768     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8769     for (cl = 0; cl < closureSize * 2; cl += 2) {
8770       const PetscInt p = closure[cl];
8771       if ((p >= vStart) && (p < vEnd)) ++Nv;
8772     }
8773     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8774     /* Special Case: Tensor faces with identified vertices */
8775     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8776       PetscInt unsplit;
8777 
8778       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8779       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8780     }
8781     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));
8782   }
8783   PetscFunctionReturn(0);
8784 }
8785 
8786 /*@
8787   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8788 
8789   Collective on dm
8790 
8791   Input Parameters:
8792 + dm - The `DMPLEX` object
8793 - cellHeight - Normally 0
8794 
8795   Level: developer
8796 
8797   Notes:
8798   This is a useful diagnostic when creating meshes programmatically.
8799   This routine is only relevant for meshes that are fully interpolated across all ranks.
8800   It will error out if a partially interpolated mesh is given on some rank.
8801   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8802 
8803   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8804 
8805 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8806 @*/
8807 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8808 {
8809   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8810   DMPlexInterpolatedFlag interpEnum;
8811 
8812   PetscFunctionBegin;
8813   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8814   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8815   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8816   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8817     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8818     PetscFunctionReturn(0);
8819   }
8820 
8821   PetscCall(DMGetDimension(dm, &dim));
8822   PetscCall(DMPlexGetDepth(dm, &depth));
8823   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8824   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8825     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8826     for (c = cStart; c < cEnd; ++c) {
8827       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8828       const DMPolytopeType *faceTypes;
8829       DMPolytopeType        ct;
8830       PetscInt              numFaces, coneSize, f;
8831       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8832 
8833       PetscCall(DMPlexGetCellType(dm, c, &ct));
8834       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8835       if (unsplit) continue;
8836       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8837       PetscCall(DMPlexGetCone(dm, c, &cone));
8838       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8839       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8840       for (cl = 0; cl < closureSize * 2; cl += 2) {
8841         const PetscInt p = closure[cl];
8842         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8843       }
8844       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8845       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);
8846       for (f = 0; f < numFaces; ++f) {
8847         DMPolytopeType fct;
8848         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8849 
8850         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8851         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8852         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8853           const PetscInt p = fclosure[cl];
8854           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8855         }
8856         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]);
8857         for (v = 0; v < fnumCorners; ++v) {
8858           if (fclosure[v] != faces[fOff + v]) {
8859             PetscInt v1;
8860 
8861             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8862             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8863             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8864             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8865             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8866             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]);
8867           }
8868         }
8869         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8870         fOff += faceSizes[f];
8871       }
8872       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8873       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8874     }
8875   }
8876   PetscFunctionReturn(0);
8877 }
8878 
8879 /*@
8880   DMPlexCheckGeometry - Check the geometry of mesh cells
8881 
8882   Input Parameter:
8883 . dm - The `DMPLEX` object
8884 
8885   Level: developer
8886 
8887   Notes:
8888   This is a useful diagnostic when creating meshes programmatically.
8889 
8890   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8891 
8892 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8893 @*/
8894 PetscErrorCode DMPlexCheckGeometry(DM dm)
8895 {
8896   Vec       coordinates;
8897   PetscReal detJ, J[9], refVol = 1.0;
8898   PetscReal vol;
8899   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8900 
8901   PetscFunctionBegin;
8902   PetscCall(DMGetDimension(dm, &dim));
8903   PetscCall(DMGetCoordinateDim(dm, &dE));
8904   if (dim != dE) PetscFunctionReturn(0);
8905   PetscCall(DMPlexGetDepth(dm, &depth));
8906   for (d = 0; d < dim; ++d) refVol *= 2.0;
8907   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8908   /* Make sure local coordinates are created, because that step is collective */
8909   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8910   if (!coordinates) PetscFunctionReturn(0);
8911   for (c = cStart; c < cEnd; ++c) {
8912     DMPolytopeType ct;
8913     PetscInt       unsplit;
8914     PetscBool      ignoreZeroVol = PETSC_FALSE;
8915 
8916     PetscCall(DMPlexGetCellType(dm, c, &ct));
8917     switch (ct) {
8918     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8919     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8920     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8921       ignoreZeroVol = PETSC_TRUE;
8922       break;
8923     default:
8924       break;
8925     }
8926     switch (ct) {
8927     case DM_POLYTOPE_TRI_PRISM:
8928     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8929     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8930     case DM_POLYTOPE_PYRAMID:
8931       continue;
8932     default:
8933       break;
8934     }
8935     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8936     if (unsplit) continue;
8937     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8938     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);
8939     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8940     /* This should work with periodicity since DG coordinates should be used */
8941     if (depth > 1) {
8942       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8943       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);
8944       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8945     }
8946   }
8947   PetscFunctionReturn(0);
8948 }
8949 
8950 /*@
8951   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8952 
8953   Collective on dm
8954 
8955   Input Parameters:
8956 + dm - The `DMPLEX` object
8957 . pointSF - The `PetscSF`, or NULL for `PointSF` attached to `DM`
8958 - allowExtraRoots - Flag to allow extra points not present in the `DM`
8959 
8960   Level: developer
8961 
8962   Notes:
8963   This is mainly intended for debugging/testing purposes.
8964 
8965   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8966 
8967   Extra roots can come from priodic cuts, where additional points appear on the boundary
8968 
8969 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
8970 @*/
8971 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
8972 {
8973   PetscInt           l, nleaves, nroots, overlap;
8974   const PetscInt    *locals;
8975   const PetscSFNode *remotes;
8976   PetscBool          distributed;
8977   MPI_Comm           comm;
8978   PetscMPIInt        rank;
8979 
8980   PetscFunctionBegin;
8981   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8982   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8983   else pointSF = dm->sf;
8984   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8985   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8986   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8987   {
8988     PetscMPIInt mpiFlag;
8989 
8990     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8991     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8992   }
8993   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8994   PetscCall(DMPlexIsDistributed(dm, &distributed));
8995   if (!distributed) {
8996     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);
8997     PetscFunctionReturn(0);
8998   }
8999   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);
9000   PetscCall(DMPlexGetOverlap(dm, &overlap));
9001 
9002   /* Check SF graph is compatible with DMPlex chart */
9003   {
9004     PetscInt pStart, pEnd, maxLeaf;
9005 
9006     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9007     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9008     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9009     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9010   }
9011 
9012   /* Check Point SF has no local points referenced */
9013   for (l = 0; l < nleaves; l++) {
9014     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);
9015   }
9016 
9017   /* Check there are no cells in interface */
9018   if (!overlap) {
9019     PetscInt cellHeight, cStart, cEnd;
9020 
9021     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9022     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9023     for (l = 0; l < nleaves; ++l) {
9024       const PetscInt point = locals ? locals[l] : l;
9025 
9026       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9027     }
9028   }
9029 
9030   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9031   {
9032     const PetscInt *rootdegree;
9033 
9034     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9035     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9036     for (l = 0; l < nleaves; ++l) {
9037       const PetscInt  point = locals ? locals[l] : l;
9038       const PetscInt *cone;
9039       PetscInt        coneSize, c, idx;
9040 
9041       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9042       PetscCall(DMPlexGetCone(dm, point, &cone));
9043       for (c = 0; c < coneSize; ++c) {
9044         if (!rootdegree[cone[c]]) {
9045           if (locals) {
9046             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9047           } else {
9048             idx = (cone[c] < nleaves) ? cone[c] : -1;
9049           }
9050           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9051         }
9052       }
9053     }
9054   }
9055   PetscFunctionReturn(0);
9056 }
9057 
9058 /*@
9059   DMPlexCheck - Perform various checks of Plex sanity
9060 
9061   Input Parameter:
9062 . dm - The `DMPLEX` object
9063 
9064   Level: developer
9065 
9066   Notes:
9067   This is a useful diagnostic when creating meshes programmatically.
9068 
9069   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
9070 
9071   Currently does not include DMPlexCheckCellShape().
9072 
9073 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, DMCreate(), DMSetFromOptions()
9074 @*/
9075 PetscErrorCode DMPlexCheck(DM dm)
9076 {
9077   PetscInt cellHeight;
9078 
9079   PetscFunctionBegin;
9080   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9081   PetscCall(DMPlexCheckSymmetry(dm));
9082   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9083   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9084   PetscCall(DMPlexCheckGeometry(dm));
9085   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9086   PetscCall(DMPlexCheckInterfaceCones(dm));
9087   PetscFunctionReturn(0);
9088 }
9089 
9090 typedef struct cell_stats {
9091   PetscReal min, max, sum, squaresum;
9092   PetscInt  count;
9093 } cell_stats_t;
9094 
9095 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9096 {
9097   PetscInt i, N = *len;
9098 
9099   for (i = 0; i < N; i++) {
9100     cell_stats_t *A = (cell_stats_t *)a;
9101     cell_stats_t *B = (cell_stats_t *)b;
9102 
9103     B->min = PetscMin(A->min, B->min);
9104     B->max = PetscMax(A->max, B->max);
9105     B->sum += A->sum;
9106     B->squaresum += A->squaresum;
9107     B->count += A->count;
9108   }
9109 }
9110 
9111 /*@
9112   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9113 
9114   Collective on dm
9115 
9116   Input Parameters:
9117 + dm        - The `DMPLEX` object
9118 . output    - If true, statistics will be displayed on stdout
9119 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9120 
9121   Level: developer
9122 
9123   Notes:
9124   This is mainly intended for debugging/testing purposes.
9125 
9126   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9127 
9128 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9129 @*/
9130 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9131 {
9132   DM           dmCoarse;
9133   cell_stats_t stats, globalStats;
9134   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9135   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9136   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9137   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9138   PetscMPIInt  rank, size;
9139 
9140   PetscFunctionBegin;
9141   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9142   stats.min = PETSC_MAX_REAL;
9143   stats.max = PETSC_MIN_REAL;
9144   stats.sum = stats.squaresum = 0.;
9145   stats.count                 = 0;
9146 
9147   PetscCallMPI(MPI_Comm_size(comm, &size));
9148   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9149   PetscCall(DMGetCoordinateDim(dm, &cdim));
9150   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9151   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9152   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9153   for (c = cStart; c < cEnd; c++) {
9154     PetscInt  i;
9155     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9156 
9157     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9158     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9159     for (i = 0; i < PetscSqr(cdim); ++i) {
9160       frobJ += J[i] * J[i];
9161       frobInvJ += invJ[i] * invJ[i];
9162     }
9163     cond2 = frobJ * frobInvJ;
9164     cond  = PetscSqrtReal(cond2);
9165 
9166     stats.min = PetscMin(stats.min, cond);
9167     stats.max = PetscMax(stats.max, cond);
9168     stats.sum += cond;
9169     stats.squaresum += cond2;
9170     stats.count++;
9171     if (output && cond > limit) {
9172       PetscSection coordSection;
9173       Vec          coordsLocal;
9174       PetscScalar *coords = NULL;
9175       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9176 
9177       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9178       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9179       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9180       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9181       for (i = 0; i < Nv / cdim; ++i) {
9182         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9183         for (d = 0; d < cdim; ++d) {
9184           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9185           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9186         }
9187         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9188       }
9189       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9190       for (cl = 0; cl < clSize * 2; cl += 2) {
9191         const PetscInt edge = closure[cl];
9192 
9193         if ((edge >= eStart) && (edge < eEnd)) {
9194           PetscReal len;
9195 
9196           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9197           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9198         }
9199       }
9200       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9201       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9202     }
9203   }
9204   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9205 
9206   if (size > 1) {
9207     PetscMPIInt  blockLengths[2] = {4, 1};
9208     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9209     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9210     MPI_Op       statReduce;
9211 
9212     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9213     PetscCallMPI(MPI_Type_commit(&statType));
9214     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9215     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9216     PetscCallMPI(MPI_Op_free(&statReduce));
9217     PetscCallMPI(MPI_Type_free(&statType));
9218   } else {
9219     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9220   }
9221   if (rank == 0) {
9222     count = globalStats.count;
9223     min   = globalStats.min;
9224     max   = globalStats.max;
9225     mean  = globalStats.sum / globalStats.count;
9226     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9227   }
9228 
9229   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));
9230   PetscCall(PetscFree2(J, invJ));
9231 
9232   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9233   if (dmCoarse) {
9234     PetscBool isplex;
9235 
9236     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9237     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9238   }
9239   PetscFunctionReturn(0);
9240 }
9241 
9242 /*@
9243   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9244   orthogonal quality below given tolerance.
9245 
9246   Collective on dm
9247 
9248   Input Parameters:
9249 + dm   - The `DMPLEX` object
9250 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9251 - atol - [0, 1] Absolute tolerance for tagging cells.
9252 
9253   Output Parameters:
9254 + OrthQual      - Vec containing orthogonal quality per cell
9255 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9256 
9257   Options Database Keys:
9258 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9259 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9260 
9261   Level: intermediate
9262 
9263   Notes:
9264   Orthogonal quality is given by the following formula:
9265 
9266   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9267 
9268   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
9269   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9270   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9271   calculating the cosine of the angle between these vectors.
9272 
9273   Orthogonal quality ranges from 1 (best) to 0 (worst).
9274 
9275   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9276   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9277 
9278   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9279 
9280 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9281 @*/
9282 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9283 {
9284   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9285   PetscInt              *idx;
9286   PetscScalar           *oqVals;
9287   const PetscScalar     *cellGeomArr, *faceGeomArr;
9288   PetscReal             *ci, *fi, *Ai;
9289   MPI_Comm               comm;
9290   Vec                    cellgeom, facegeom;
9291   DM                     dmFace, dmCell;
9292   IS                     glob;
9293   ISLocalToGlobalMapping ltog;
9294   PetscViewer            vwr;
9295 
9296   PetscFunctionBegin;
9297   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9298   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9299   PetscValidPointer(OrthQual, 4);
9300   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9301   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9302   PetscCall(DMGetDimension(dm, &nc));
9303   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9304   {
9305     DMPlexInterpolatedFlag interpFlag;
9306 
9307     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9308     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9309       PetscMPIInt rank;
9310 
9311       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9312       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9313     }
9314   }
9315   if (OrthQualLabel) {
9316     PetscValidPointer(OrthQualLabel, 5);
9317     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9318     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9319   } else {
9320     *OrthQualLabel = NULL;
9321   }
9322   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9323   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9324   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9325   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9326   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9327   PetscCall(VecCreate(comm, OrthQual));
9328   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9329   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9330   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9331   PetscCall(VecSetUp(*OrthQual));
9332   PetscCall(ISDestroy(&glob));
9333   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9334   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9335   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9336   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9337   PetscCall(VecGetDM(cellgeom, &dmCell));
9338   PetscCall(VecGetDM(facegeom, &dmFace));
9339   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9340   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9341     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9342     PetscInt         cellarr[2], *adj = NULL;
9343     PetscScalar     *cArr, *fArr;
9344     PetscReal        minvalc = 1.0, minvalf = 1.0;
9345     PetscFVCellGeom *cg;
9346 
9347     idx[cellIter] = cell - cStart;
9348     cellarr[0]    = cell;
9349     /* Make indexing into cellGeom easier */
9350     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9351     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9352     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9353     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9354     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9355       PetscInt         i;
9356       const PetscInt   neigh  = adj[cellneigh];
9357       PetscReal        normci = 0, normfi = 0, normai = 0;
9358       PetscFVCellGeom *cgneigh;
9359       PetscFVFaceGeom *fg;
9360 
9361       /* Don't count ourselves in the neighbor list */
9362       if (neigh == cell) continue;
9363       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9364       cellarr[1] = neigh;
9365       {
9366         PetscInt        numcovpts;
9367         const PetscInt *covpts;
9368 
9369         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9370         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9371         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9372       }
9373 
9374       /* Compute c_i, f_i and their norms */
9375       for (i = 0; i < nc; i++) {
9376         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9377         fi[i] = fg->centroid[i] - cg->centroid[i];
9378         Ai[i] = fg->normal[i];
9379         normci += PetscPowReal(ci[i], 2);
9380         normfi += PetscPowReal(fi[i], 2);
9381         normai += PetscPowReal(Ai[i], 2);
9382       }
9383       normci = PetscSqrtReal(normci);
9384       normfi = PetscSqrtReal(normfi);
9385       normai = PetscSqrtReal(normai);
9386 
9387       /* Normalize and compute for each face-cell-normal pair */
9388       for (i = 0; i < nc; i++) {
9389         ci[i] = ci[i] / normci;
9390         fi[i] = fi[i] / normfi;
9391         Ai[i] = Ai[i] / normai;
9392         /* PetscAbs because I don't know if normals are guaranteed to point out */
9393         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9394         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9395       }
9396       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9397       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9398     }
9399     PetscCall(PetscFree(adj));
9400     PetscCall(PetscFree2(cArr, fArr));
9401     /* Defer to cell if they're equal */
9402     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9403     if (OrthQualLabel) {
9404       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9405     }
9406   }
9407   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9408   PetscCall(VecAssemblyBegin(*OrthQual));
9409   PetscCall(VecAssemblyEnd(*OrthQual));
9410   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9411   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9412   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9413   if (OrthQualLabel) {
9414     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9415   }
9416   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9417   PetscCall(PetscViewerDestroy(&vwr));
9418   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9419   PetscFunctionReturn(0);
9420 }
9421 
9422 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9423  * interpolator construction */
9424 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9425 {
9426   PetscSection section, newSection, gsection;
9427   PetscSF      sf;
9428   PetscBool    hasConstraints, ghasConstraints;
9429 
9430   PetscFunctionBegin;
9431   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9432   PetscValidPointer(odm, 2);
9433   PetscCall(DMGetLocalSection(dm, &section));
9434   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9435   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9436   if (!ghasConstraints) {
9437     PetscCall(PetscObjectReference((PetscObject)dm));
9438     *odm = dm;
9439     PetscFunctionReturn(0);
9440   }
9441   PetscCall(DMClone(dm, odm));
9442   PetscCall(DMCopyFields(dm, *odm));
9443   PetscCall(DMGetLocalSection(*odm, &newSection));
9444   PetscCall(DMGetPointSF(*odm, &sf));
9445   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9446   PetscCall(DMSetGlobalSection(*odm, gsection));
9447   PetscCall(PetscSectionDestroy(&gsection));
9448   PetscFunctionReturn(0);
9449 }
9450 
9451 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9452 {
9453   DM        dmco, dmfo;
9454   Mat       interpo;
9455   Vec       rscale;
9456   Vec       cglobalo, clocal;
9457   Vec       fglobal, fglobalo, flocal;
9458   PetscBool regular;
9459 
9460   PetscFunctionBegin;
9461   PetscCall(DMGetFullDM(dmc, &dmco));
9462   PetscCall(DMGetFullDM(dmf, &dmfo));
9463   PetscCall(DMSetCoarseDM(dmfo, dmco));
9464   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9465   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9466   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9467   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9468   PetscCall(DMCreateLocalVector(dmc, &clocal));
9469   PetscCall(VecSet(cglobalo, 0.));
9470   PetscCall(VecSet(clocal, 0.));
9471   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9472   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9473   PetscCall(DMCreateLocalVector(dmf, &flocal));
9474   PetscCall(VecSet(fglobal, 0.));
9475   PetscCall(VecSet(fglobalo, 0.));
9476   PetscCall(VecSet(flocal, 0.));
9477   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9478   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9479   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9480   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9481   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9482   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9483   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9484   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9485   *shift = fglobal;
9486   PetscCall(VecDestroy(&flocal));
9487   PetscCall(VecDestroy(&fglobalo));
9488   PetscCall(VecDestroy(&clocal));
9489   PetscCall(VecDestroy(&cglobalo));
9490   PetscCall(VecDestroy(&rscale));
9491   PetscCall(MatDestroy(&interpo));
9492   PetscCall(DMDestroy(&dmfo));
9493   PetscCall(DMDestroy(&dmco));
9494   PetscFunctionReturn(0);
9495 }
9496 
9497 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9498 {
9499   PetscObject shifto;
9500   Vec         shift;
9501 
9502   PetscFunctionBegin;
9503   if (!interp) {
9504     Vec rscale;
9505 
9506     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9507     PetscCall(VecDestroy(&rscale));
9508   } else {
9509     PetscCall(PetscObjectReference((PetscObject)interp));
9510   }
9511   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9512   if (!shifto) {
9513     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9514     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9515     shifto = (PetscObject)shift;
9516     PetscCall(VecDestroy(&shift));
9517   }
9518   shift = (Vec)shifto;
9519   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9520   PetscCall(VecAXPY(fineSol, 1.0, shift));
9521   PetscCall(MatDestroy(&interp));
9522   PetscFunctionReturn(0);
9523 }
9524 
9525 /* Pointwise interpolation
9526      Just code FEM for now
9527      u^f = I u^c
9528      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9529      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9530      I_{ij} = psi^f_i phi^c_j
9531 */
9532 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9533 {
9534   PetscSection gsc, gsf;
9535   PetscInt     m, n;
9536   void        *ctx;
9537   DM           cdm;
9538   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9539 
9540   PetscFunctionBegin;
9541   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9542   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9543   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9544   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9545 
9546   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9547   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9548   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9549   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9550   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9551 
9552   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9553   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9554   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9555   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9556   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9557   if (scaling) {
9558     /* Use naive scaling */
9559     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9560   }
9561   PetscFunctionReturn(0);
9562 }
9563 
9564 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9565 {
9566   VecScatter ctx;
9567 
9568   PetscFunctionBegin;
9569   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9570   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9571   PetscCall(VecScatterDestroy(&ctx));
9572   PetscFunctionReturn(0);
9573 }
9574 
9575 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[])
9576 {
9577   const PetscInt Nc = uOff[1] - uOff[0];
9578   PetscInt       c;
9579   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9580 }
9581 
9582 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9583 {
9584   DM           dmc;
9585   PetscDS      ds;
9586   Vec          ones, locmass;
9587   IS           cellIS;
9588   PetscFormKey key;
9589   PetscInt     depth;
9590 
9591   PetscFunctionBegin;
9592   PetscCall(DMClone(dm, &dmc));
9593   PetscCall(DMCopyDisc(dm, dmc));
9594   PetscCall(DMGetDS(dmc, &ds));
9595   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9596   PetscCall(DMCreateGlobalVector(dmc, mass));
9597   PetscCall(DMGetLocalVector(dmc, &ones));
9598   PetscCall(DMGetLocalVector(dmc, &locmass));
9599   PetscCall(DMPlexGetDepth(dmc, &depth));
9600   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9601   PetscCall(VecSet(locmass, 0.0));
9602   PetscCall(VecSet(ones, 1.0));
9603   key.label = NULL;
9604   key.value = 0;
9605   key.field = 0;
9606   key.part  = 0;
9607   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9608   PetscCall(ISDestroy(&cellIS));
9609   PetscCall(VecSet(*mass, 0.0));
9610   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9611   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9612   PetscCall(DMRestoreLocalVector(dmc, &ones));
9613   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9614   PetscCall(DMDestroy(&dmc));
9615   PetscFunctionReturn(0);
9616 }
9617 
9618 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9619 {
9620   PetscSection gsc, gsf;
9621   PetscInt     m, n;
9622   void        *ctx;
9623   DM           cdm;
9624   PetscBool    regular;
9625 
9626   PetscFunctionBegin;
9627   if (dmFine == dmCoarse) {
9628     DM            dmc;
9629     PetscDS       ds;
9630     PetscWeakForm wf;
9631     Vec           u;
9632     IS            cellIS;
9633     PetscFormKey  key;
9634     PetscInt      depth;
9635 
9636     PetscCall(DMClone(dmFine, &dmc));
9637     PetscCall(DMCopyDisc(dmFine, dmc));
9638     PetscCall(DMGetDS(dmc, &ds));
9639     PetscCall(PetscDSGetWeakForm(ds, &wf));
9640     PetscCall(PetscWeakFormClear(wf));
9641     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9642     PetscCall(DMCreateMatrix(dmc, mass));
9643     PetscCall(DMGetLocalVector(dmc, &u));
9644     PetscCall(DMPlexGetDepth(dmc, &depth));
9645     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9646     PetscCall(MatZeroEntries(*mass));
9647     key.label = NULL;
9648     key.value = 0;
9649     key.field = 0;
9650     key.part  = 0;
9651     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9652     PetscCall(ISDestroy(&cellIS));
9653     PetscCall(DMRestoreLocalVector(dmc, &u));
9654     PetscCall(DMDestroy(&dmc));
9655   } else {
9656     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9657     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9658     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9659     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9660 
9661     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9662     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9663     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9664     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9665 
9666     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9667     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9668     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9669     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9670   }
9671   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9672   PetscFunctionReturn(0);
9673 }
9674 
9675 /*@
9676   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9677 
9678   Input Parameter:
9679 . dm - The `DMPLEX` object
9680 
9681   Output Parameter:
9682 . regular - The flag
9683 
9684   Level: intermediate
9685 
9686 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9687 @*/
9688 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9689 {
9690   PetscFunctionBegin;
9691   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9692   PetscValidBoolPointer(regular, 2);
9693   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9694   PetscFunctionReturn(0);
9695 }
9696 
9697 /*@
9698   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9699 
9700   Input Parameters:
9701 + dm - The `DMPLEX` object
9702 - regular - The flag
9703 
9704   Level: intermediate
9705 
9706 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9707 @*/
9708 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9709 {
9710   PetscFunctionBegin;
9711   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9712   ((DM_Plex *)dm->data)->regularRefinement = regular;
9713   PetscFunctionReturn(0);
9714 }
9715 
9716 /*@
9717   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9718   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9719 
9720   Not Collective
9721 
9722   Input Parameter:
9723 . dm - The `DMPLEX` object
9724 
9725   Output Parameters:
9726 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9727 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9728 
9729   Level: intermediate
9730 
9731 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9732 @*/
9733 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9734 {
9735   DM_Plex *plex = (DM_Plex *)dm->data;
9736 
9737   PetscFunctionBegin;
9738   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9739   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9740   if (anchorSection) *anchorSection = plex->anchorSection;
9741   if (anchorIS) *anchorIS = plex->anchorIS;
9742   PetscFunctionReturn(0);
9743 }
9744 
9745 /*@
9746   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9747   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9748   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9749 
9750   Collective on dm
9751 
9752   Input Parameters:
9753 + dm - The `DMPLEX` object
9754 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9755                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9756 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9757 
9758   Level: intermediate
9759 
9760   Notes:
9761   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9762   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9763 
9764   The reference counts of anchorSection and anchorIS are incremented.
9765 
9766 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9767 @*/
9768 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9769 {
9770   DM_Plex    *plex = (DM_Plex *)dm->data;
9771   PetscMPIInt result;
9772 
9773   PetscFunctionBegin;
9774   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9775   if (anchorSection) {
9776     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9777     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9778     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9779   }
9780   if (anchorIS) {
9781     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9782     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9783     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9784   }
9785 
9786   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9787   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9788   plex->anchorSection = anchorSection;
9789 
9790   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9791   PetscCall(ISDestroy(&plex->anchorIS));
9792   plex->anchorIS = anchorIS;
9793 
9794   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9795     PetscInt        size, a, pStart, pEnd;
9796     const PetscInt *anchors;
9797 
9798     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9799     PetscCall(ISGetLocalSize(anchorIS, &size));
9800     PetscCall(ISGetIndices(anchorIS, &anchors));
9801     for (a = 0; a < size; a++) {
9802       PetscInt p;
9803 
9804       p = anchors[a];
9805       if (p >= pStart && p < pEnd) {
9806         PetscInt dof;
9807 
9808         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9809         if (dof) {
9810           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9811           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9812         }
9813       }
9814     }
9815     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9816   }
9817   /* reset the generic constraints */
9818   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9819   PetscFunctionReturn(0);
9820 }
9821 
9822 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9823 {
9824   PetscSection anchorSection;
9825   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9826 
9827   PetscFunctionBegin;
9828   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9829   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9830   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9831   PetscCall(PetscSectionGetNumFields(section, &numFields));
9832   if (numFields) {
9833     PetscInt f;
9834     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9835 
9836     for (f = 0; f < numFields; f++) {
9837       PetscInt numComp;
9838 
9839       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9840       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9841     }
9842   }
9843   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9844   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9845   pStart = PetscMax(pStart, sStart);
9846   pEnd   = PetscMin(pEnd, sEnd);
9847   pEnd   = PetscMax(pStart, pEnd);
9848   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9849   for (p = pStart; p < pEnd; p++) {
9850     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9851     if (dof) {
9852       PetscCall(PetscSectionGetDof(section, p, &dof));
9853       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9854       for (f = 0; f < numFields; f++) {
9855         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9856         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9857       }
9858     }
9859   }
9860   PetscCall(PetscSectionSetUp(*cSec));
9861   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9862   PetscFunctionReturn(0);
9863 }
9864 
9865 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9866 {
9867   PetscSection    aSec;
9868   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9869   const PetscInt *anchors;
9870   PetscInt        numFields, f;
9871   IS              aIS;
9872   MatType         mtype;
9873   PetscBool       iscuda, iskokkos;
9874 
9875   PetscFunctionBegin;
9876   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9877   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9878   PetscCall(PetscSectionGetStorageSize(section, &n));
9879   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9880   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9881   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9882   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9883   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9884   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9885   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9886   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9887   else mtype = MATSEQAIJ;
9888   PetscCall(MatSetType(*cMat, mtype));
9889   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9890   PetscCall(ISGetIndices(aIS, &anchors));
9891   /* cSec will be a subset of aSec and section */
9892   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9893   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9894   PetscCall(PetscMalloc1(m + 1, &i));
9895   i[0] = 0;
9896   PetscCall(PetscSectionGetNumFields(section, &numFields));
9897   for (p = pStart; p < pEnd; p++) {
9898     PetscInt rDof, rOff, r;
9899 
9900     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9901     if (!rDof) continue;
9902     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9903     if (numFields) {
9904       for (f = 0; f < numFields; f++) {
9905         annz = 0;
9906         for (r = 0; r < rDof; r++) {
9907           a = anchors[rOff + r];
9908           if (a < sStart || a >= sEnd) continue;
9909           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9910           annz += aDof;
9911         }
9912         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9913         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9914         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9915       }
9916     } else {
9917       annz = 0;
9918       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9919       for (q = 0; q < dof; q++) {
9920         a = anchors[rOff + q];
9921         if (a < sStart || a >= sEnd) continue;
9922         PetscCall(PetscSectionGetDof(section, a, &aDof));
9923         annz += aDof;
9924       }
9925       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9926       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9927       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9928     }
9929   }
9930   nnz = i[m];
9931   PetscCall(PetscMalloc1(nnz, &j));
9932   offset = 0;
9933   for (p = pStart; p < pEnd; p++) {
9934     if (numFields) {
9935       for (f = 0; f < numFields; f++) {
9936         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9937         for (q = 0; q < dof; q++) {
9938           PetscInt rDof, rOff, r;
9939           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9940           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9941           for (r = 0; r < rDof; r++) {
9942             PetscInt s;
9943 
9944             a = anchors[rOff + r];
9945             if (a < sStart || a >= sEnd) continue;
9946             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9947             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9948             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9949           }
9950         }
9951       }
9952     } else {
9953       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9954       for (q = 0; q < dof; q++) {
9955         PetscInt rDof, rOff, r;
9956         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9957         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9958         for (r = 0; r < rDof; r++) {
9959           PetscInt s;
9960 
9961           a = anchors[rOff + r];
9962           if (a < sStart || a >= sEnd) continue;
9963           PetscCall(PetscSectionGetDof(section, a, &aDof));
9964           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9965           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9966         }
9967       }
9968     }
9969   }
9970   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9971   PetscCall(PetscFree(i));
9972   PetscCall(PetscFree(j));
9973   PetscCall(ISRestoreIndices(aIS, &anchors));
9974   PetscFunctionReturn(0);
9975 }
9976 
9977 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9978 {
9979   DM_Plex     *plex = (DM_Plex *)dm->data;
9980   PetscSection anchorSection, section, cSec;
9981   Mat          cMat;
9982 
9983   PetscFunctionBegin;
9984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9985   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9986   if (anchorSection) {
9987     PetscInt Nf;
9988 
9989     PetscCall(DMGetLocalSection(dm, &section));
9990     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9991     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9992     PetscCall(DMGetNumFields(dm, &Nf));
9993     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9994     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9995     PetscCall(PetscSectionDestroy(&cSec));
9996     PetscCall(MatDestroy(&cMat));
9997   }
9998   PetscFunctionReturn(0);
9999 }
10000 
10001 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10002 {
10003   IS           subis;
10004   PetscSection section, subsection;
10005 
10006   PetscFunctionBegin;
10007   PetscCall(DMGetLocalSection(dm, &section));
10008   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10009   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10010   /* Create subdomain */
10011   PetscCall(DMPlexFilter(dm, label, value, subdm));
10012   /* Create submodel */
10013   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10014   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10015   PetscCall(DMSetLocalSection(*subdm, subsection));
10016   PetscCall(PetscSectionDestroy(&subsection));
10017   PetscCall(DMCopyDisc(dm, *subdm));
10018   /* Create map from submodel to global model */
10019   if (is) {
10020     PetscSection    sectionGlobal, subsectionGlobal;
10021     IS              spIS;
10022     const PetscInt *spmap;
10023     PetscInt       *subIndices;
10024     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10025     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10026 
10027     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10028     PetscCall(ISGetIndices(spIS, &spmap));
10029     PetscCall(PetscSectionGetNumFields(section, &Nf));
10030     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10031     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10032     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10033     for (p = pStart; p < pEnd; ++p) {
10034       PetscInt gdof, pSubSize = 0;
10035 
10036       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10037       if (gdof > 0) {
10038         for (f = 0; f < Nf; ++f) {
10039           PetscInt fdof, fcdof;
10040 
10041           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10042           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10043           pSubSize += fdof - fcdof;
10044         }
10045         subSize += pSubSize;
10046         if (pSubSize) {
10047           if (bs < 0) {
10048             bs = pSubSize;
10049           } else if (bs != pSubSize) {
10050             /* Layout does not admit a pointwise block size */
10051             bs = 1;
10052           }
10053         }
10054       }
10055     }
10056     /* Must have same blocksize on all procs (some might have no points) */
10057     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10058     bsLocal[1] = bs;
10059     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10060     if (bsMinMax[0] != bsMinMax[1]) {
10061       bs = 1;
10062     } else {
10063       bs = bsMinMax[0];
10064     }
10065     PetscCall(PetscMalloc1(subSize, &subIndices));
10066     for (p = pStart; p < pEnd; ++p) {
10067       PetscInt gdof, goff;
10068 
10069       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10070       if (gdof > 0) {
10071         const PetscInt point = spmap[p];
10072 
10073         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10074         for (f = 0; f < Nf; ++f) {
10075           PetscInt fdof, fcdof, fc, f2, poff = 0;
10076 
10077           /* Can get rid of this loop by storing field information in the global section */
10078           for (f2 = 0; f2 < f; ++f2) {
10079             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10080             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10081             poff += fdof - fcdof;
10082           }
10083           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10084           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10085           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10086         }
10087       }
10088     }
10089     PetscCall(ISRestoreIndices(spIS, &spmap));
10090     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10091     if (bs > 1) {
10092       /* We need to check that the block size does not come from non-contiguous fields */
10093       PetscInt i, j, set = 1;
10094       for (i = 0; i < subSize; i += bs) {
10095         for (j = 0; j < bs; ++j) {
10096           if (subIndices[i + j] != subIndices[i] + j) {
10097             set = 0;
10098             break;
10099           }
10100         }
10101       }
10102       if (set) PetscCall(ISSetBlockSize(*is, bs));
10103     }
10104     /* Attach nullspace */
10105     for (f = 0; f < Nf; ++f) {
10106       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10107       if ((*subdm)->nullspaceConstructors[f]) break;
10108     }
10109     if (f < Nf) {
10110       MatNullSpace nullSpace;
10111       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10112 
10113       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10114       PetscCall(MatNullSpaceDestroy(&nullSpace));
10115     }
10116   }
10117   PetscFunctionReturn(0);
10118 }
10119 
10120 /*@
10121   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10122 
10123   Input Parameters:
10124 + dm - The `DM`
10125 - dummy - unused argument
10126 
10127   Options Database Key:
10128 . -dm_plex_monitor_throughput - Activate the monitor
10129 
10130   Level: developer
10131 
10132 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10133 @*/
10134 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10135 {
10136 #if defined(PETSC_USE_LOG)
10137   PetscStageLog      stageLog;
10138   PetscLogEvent      event;
10139   PetscLogStage      stage;
10140   PetscEventPerfInfo eventInfo;
10141   PetscReal          cellRate, flopRate;
10142   PetscInt           cStart, cEnd, Nf, N;
10143   const char        *name;
10144 #endif
10145 
10146   PetscFunctionBegin;
10147   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10148 #if defined(PETSC_USE_LOG)
10149   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10150   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10151   PetscCall(DMGetNumFields(dm, &Nf));
10152   PetscCall(PetscLogGetStageLog(&stageLog));
10153   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10154   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10155   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10156   N        = (cEnd - cStart) * Nf * eventInfo.count;
10157   flopRate = eventInfo.flops / eventInfo.time;
10158   cellRate = N / eventInfo.time;
10159   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)));
10160 #else
10161   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10162 #endif
10163   PetscFunctionReturn(0);
10164 }
10165