xref: /petsc/src/dm/impls/plex/plex.c (revision dbff2e812d3f1975f2fc386c980b1b5f65f538ff)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm - The `DMPLEX` object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(PETSC_SUCCESS);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(PETSC_SUCCESS);
49 }
50 
51 /*@
52   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
53 
54   Input Parameters:
55 + dm     - The `DMPLEX` object
56 - height - The cell height in the Plex, 0 is the default
57 
58   Output Parameters:
59 + cStart - The first "normal" cell
60 - cEnd   - The upper bound on "normal"" cells
61 
62   Level: developer
63 
64   Note:
65   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
66 
67 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(PETSC_SUCCESS);
106 }
107 
108 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
109 {
110   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
111   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
112 
113   PetscFunctionBegin;
114   *ft = PETSC_VTK_INVALID;
115   PetscCall(DMGetCoordinateDim(dm, &cdim));
116   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
118   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
119   if (field >= 0) {
120     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
121     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
122   } else {
123     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
124     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
125   }
126   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(PETSC_SUCCESS);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective
154 
155   Input Parameters:
156 + dm     - The `DMPLEX` object
157 . n      - The number of vectors
158 . u      - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
202         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
203         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(PETSC_SUCCESS);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *array;
253   PetscReal          lbound[3], ubound[3];
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
278   PetscCall(PetscDrawClear(draw));
279 
280   /* Could implement something like DMDASelectFields() */
281   for (f = 0; f < Nf; ++f) {
282     DM          fdm = dm;
283     Vec         fv  = v;
284     IS          fis;
285     char        prefix[PETSC_MAX_PATH_LEN];
286     const char *fname;
287 
288     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
289     PetscCall(PetscSectionGetFieldName(s, f, &fname));
290 
291     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
292     else prefix[0] = '\0';
293     if (Nf > 1) {
294       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
295       PetscCall(VecGetSubVector(v, fis, &fv));
296       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
297       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
298     }
299     for (comp = 0; comp < Nc; ++comp, ++w) {
300       PetscInt nmax = 2;
301 
302       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
303       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
304       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
305       PetscCall(PetscDrawSetTitle(draw, title));
306 
307       /* TODO Get max and min only for this component */
308       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
309       if (!flg) {
310         PetscCall(VecMin(fv, NULL, &vbound[0]));
311         PetscCall(VecMax(fv, NULL, &vbound[1]));
312         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
313       }
314 
315       PetscCall(PetscDrawGetPopup(draw, &popup));
316       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
317       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
318       PetscCall(VecGetArrayRead(fv, &array));
319       for (c = cStart; c < cEnd; ++c) {
320         PetscScalar       *coords = NULL, *a = NULL;
321         const PetscScalar *coords_arr;
322         PetscBool          isDG;
323         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
324 
325         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
326         if (a) {
327           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
328           color[1] = color[2] = color[3] = color[0];
329         } else {
330           PetscScalar *vals = NULL;
331           PetscInt     numVals, va;
332 
333           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
334           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
335           switch (numVals / Nc) {
336           case 3: /* P1 Triangle */
337           case 4: /* P1 Quadrangle */
338             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
339             break;
340           case 6: /* P2 Triangle */
341           case 8: /* P2 Quadrangle */
342             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
343             break;
344           default:
345             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
346           }
347           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
348         }
349         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
350         switch (numCoords) {
351         case 6:
352         case 12: /* Localized triangle */
353           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
354           break;
355         case 8:
356         case 16: /* Localized quadrilateral */
357           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
358           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
359           break;
360         default:
361           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
362         }
363         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
364       }
365       PetscCall(VecRestoreArrayRead(fv, &array));
366       PetscCall(PetscDrawFlush(draw));
367       PetscCall(PetscDrawPause(draw));
368       PetscCall(PetscDrawSave(draw));
369     }
370     if (Nf > 1) {
371       PetscCall(VecRestoreSubVector(v, fis, &fv));
372       PetscCall(ISDestroy(&fis));
373       PetscCall(DMDestroy(&fdm));
374     }
375   }
376   PetscFunctionReturn(PETSC_SUCCESS);
377 }
378 
379 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
380 {
381   DM        dm;
382   PetscDraw draw;
383   PetscInt  dim;
384   PetscBool isnull;
385 
386   PetscFunctionBegin;
387   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
388   PetscCall(PetscDrawIsNull(draw, &isnull));
389   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
390 
391   PetscCall(VecGetDM(v, &dm));
392   PetscCall(DMGetCoordinateDim(dm, &dim));
393   switch (dim) {
394   case 1:
395     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
396     break;
397   case 2:
398     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
399     break;
400   default:
401     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
402   }
403   PetscFunctionReturn(PETSC_SUCCESS);
404 }
405 
406 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
407 {
408   DM                      dm;
409   Vec                     locv;
410   const char             *name;
411   PetscSection            section;
412   PetscInt                pStart, pEnd;
413   PetscInt                numFields;
414   PetscViewerVTKFieldType ft;
415 
416   PetscFunctionBegin;
417   PetscCall(VecGetDM(v, &dm));
418   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
419   PetscCall(PetscObjectGetName((PetscObject)v, &name));
420   PetscCall(PetscObjectSetName((PetscObject)locv, name));
421   PetscCall(VecCopy(v, locv));
422   PetscCall(DMGetLocalSection(dm, &section));
423   PetscCall(PetscSectionGetNumFields(section, &numFields));
424   if (!numFields) {
425     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
426     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
427   } else {
428     PetscInt f;
429 
430     for (f = 0; f < numFields; f++) {
431       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
432       if (ft == PETSC_VTK_INVALID) continue;
433       PetscCall(PetscObjectReference((PetscObject)locv));
434       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
435     }
436     PetscCall(VecDestroy(&locv));
437   }
438   PetscFunctionReturn(PETSC_SUCCESS);
439 }
440 
441 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
442 {
443   DM        dm;
444   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
445 
446   PetscFunctionBegin;
447   PetscCall(VecGetDM(v, &dm));
448   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
449   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
450   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
453   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
454   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
455     PetscInt    i, numFields;
456     PetscObject fe;
457     PetscBool   fem  = PETSC_FALSE;
458     Vec         locv = v;
459     const char *name;
460     PetscInt    step;
461     PetscReal   time;
462 
463     PetscCall(DMGetNumFields(dm, &numFields));
464     for (i = 0; i < numFields; i++) {
465       PetscCall(DMGetField(dm, i, NULL, &fe));
466       if (fe->classid == PETSCFE_CLASSID) {
467         fem = PETSC_TRUE;
468         break;
469       }
470     }
471     if (fem) {
472       PetscObject isZero;
473 
474       PetscCall(DMGetLocalVector(dm, &locv));
475       PetscCall(PetscObjectGetName((PetscObject)v, &name));
476       PetscCall(PetscObjectSetName((PetscObject)locv, name));
477       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
478       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
479       PetscCall(VecCopy(v, locv));
480       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
481       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
482     }
483     if (isvtk) {
484       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
485     } else if (ishdf5) {
486 #if defined(PETSC_HAVE_HDF5)
487       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
488 #else
489       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
490 #endif
491     } else if (isdraw) {
492       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
493     } else if (isglvis) {
494       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
495       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
496       PetscCall(VecView_GLVis(locv, viewer));
497     } else if (iscgns) {
498 #if defined(PETSC_HAVE_CGNS)
499       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
500 #else
501       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
502 #endif
503     }
504     if (fem) {
505       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
506       PetscCall(DMRestoreLocalVector(dm, &locv));
507     }
508   } else {
509     PetscBool isseq;
510 
511     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
512     if (isseq) PetscCall(VecView_Seq(v, viewer));
513     else PetscCall(VecView_MPI(v, viewer));
514   }
515   PetscFunctionReturn(PETSC_SUCCESS);
516 }
517 
518 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
519 {
520   DM        dm;
521   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
527   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
528   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
529   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
532   if (isvtk || isdraw || isglvis || iscgns) {
533     Vec         locv;
534     PetscObject isZero;
535     const char *name;
536 
537     PetscCall(DMGetLocalVector(dm, &locv));
538     PetscCall(PetscObjectGetName((PetscObject)v, &name));
539     PetscCall(PetscObjectSetName((PetscObject)locv, name));
540     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
541     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
542     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
543     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
544     PetscCall(VecView_Plex_Local(locv, viewer));
545     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
546     PetscCall(DMRestoreLocalVector(dm, &locv));
547   } else if (ishdf5) {
548 #if defined(PETSC_HAVE_HDF5)
549     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
550 #else
551     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
552 #endif
553   } else if (isexodusii) {
554 #if defined(PETSC_HAVE_EXODUSII)
555     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
556 #else
557     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
558 #endif
559   } else {
560     PetscBool isseq;
561 
562     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
563     if (isseq) PetscCall(VecView_Seq(v, viewer));
564     else PetscCall(VecView_MPI(v, viewer));
565   }
566   PetscFunctionReturn(PETSC_SUCCESS);
567 }
568 
569 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
570 {
571   DM                dm;
572   MPI_Comm          comm;
573   PetscViewerFormat format;
574   Vec               v;
575   PetscBool         isvtk, ishdf5;
576 
577   PetscFunctionBegin;
578   PetscCall(VecGetDM(originalv, &dm));
579   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
580   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
581   PetscCall(PetscViewerGetFormat(viewer, &format));
582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
583   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
584   if (format == PETSC_VIEWER_NATIVE) {
585     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
586     /* this need a better fix */
587     if (dm->useNatural) {
588       if (dm->sfNatural) {
589         const char *vecname;
590         PetscInt    n, nroots;
591 
592         PetscCall(VecGetLocalSize(originalv, &n));
593         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
594         if (n == nroots) {
595           PetscCall(DMPlexCreateNaturalVector(dm, &v));
596           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
597           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
598           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
599           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
600         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
601       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
602     } else v = originalv;
603   } else v = originalv;
604 
605   if (ishdf5) {
606 #if defined(PETSC_HAVE_HDF5)
607     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
608 #else
609     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
610 #endif
611   } else if (isvtk) {
612     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
613   } else {
614     PetscBool isseq;
615 
616     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
617     if (isseq) PetscCall(VecView_Seq(v, viewer));
618     else PetscCall(VecView_MPI(v, viewer));
619   }
620   if (v != originalv) PetscCall(VecDestroy(&v));
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
625 {
626   DM        dm;
627   PetscBool ishdf5;
628 
629   PetscFunctionBegin;
630   PetscCall(VecGetDM(v, &dm));
631   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
633   if (ishdf5) {
634     DM          dmBC;
635     Vec         gv;
636     const char *name;
637 
638     PetscCall(DMGetOutputDM(dm, &dmBC));
639     PetscCall(DMGetGlobalVector(dmBC, &gv));
640     PetscCall(PetscObjectGetName((PetscObject)v, &name));
641     PetscCall(PetscObjectSetName((PetscObject)gv, name));
642     PetscCall(VecLoad_Default(gv, viewer));
643     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
644     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
645     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
646   } else PetscCall(VecLoad_Default(v, viewer));
647   PetscFunctionReturn(PETSC_SUCCESS);
648 }
649 
650 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
651 {
652   DM        dm;
653   PetscBool ishdf5, isexodusii;
654 
655   PetscFunctionBegin;
656   PetscCall(VecGetDM(v, &dm));
657   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
659   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
660   if (ishdf5) {
661 #if defined(PETSC_HAVE_HDF5)
662     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
665 #endif
666   } else if (isexodusii) {
667 #if defined(PETSC_HAVE_EXODUSII)
668     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
669 #else
670     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
671 #endif
672   } else PetscCall(VecLoad_Default(v, viewer));
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   PetscViewerFormat format;
680   PetscBool         ishdf5;
681 
682   PetscFunctionBegin;
683   PetscCall(VecGetDM(originalv, &dm));
684   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
685   PetscCall(PetscViewerGetFormat(viewer, &format));
686   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
687   if (format == PETSC_VIEWER_NATIVE) {
688     if (dm->useNatural) {
689       if (dm->sfNatural) {
690         if (ishdf5) {
691 #if defined(PETSC_HAVE_HDF5)
692           Vec         v;
693           const char *vecname;
694 
695           PetscCall(DMPlexCreateNaturalVector(dm, &v));
696           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
697           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
698           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
699           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
700           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
701           PetscCall(VecDestroy(&v));
702 #else
703           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
704 #endif
705         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
706       }
707     } else PetscCall(VecLoad_Default(originalv, viewer));
708   }
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
713 {
714   PetscSection       coordSection;
715   Vec                coordinates;
716   DMLabel            depthLabel, celltypeLabel;
717   const char        *name[4];
718   const PetscScalar *a;
719   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
720 
721   PetscFunctionBegin;
722   PetscCall(DMGetDimension(dm, &dim));
723   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
724   PetscCall(DMGetCoordinateSection(dm, &coordSection));
725   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
726   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
727   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
728   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
729   PetscCall(VecGetArrayRead(coordinates, &a));
730   name[0]       = "vertex";
731   name[1]       = "edge";
732   name[dim - 1] = "face";
733   name[dim]     = "cell";
734   for (c = cStart; c < cEnd; ++c) {
735     PetscInt *closure = NULL;
736     PetscInt  closureSize, cl, ct;
737 
738     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
739     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
740     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
741     PetscCall(PetscViewerASCIIPushTab(viewer));
742     for (cl = 0; cl < closureSize * 2; cl += 2) {
743       PetscInt point = closure[cl], depth, dof, off, d, p;
744 
745       if ((point < pStart) || (point >= pEnd)) continue;
746       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
747       if (!dof) continue;
748       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
749       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
750       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
751       for (p = 0; p < dof / dim; ++p) {
752         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
753         for (d = 0; d < dim; ++d) {
754           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
755           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
756         }
757         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
758       }
759       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
760     }
761     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
762     PetscCall(PetscViewerASCIIPopTab(viewer));
763   }
764   PetscCall(VecRestoreArrayRead(coordinates, &a));
765   PetscFunctionReturn(PETSC_SUCCESS);
766 }
767 
768 typedef enum {
769   CS_CARTESIAN,
770   CS_POLAR,
771   CS_CYLINDRICAL,
772   CS_SPHERICAL
773 } CoordSystem;
774 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
775 
776 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
777 {
778   PetscInt i;
779 
780   PetscFunctionBegin;
781   if (dim > 3) {
782     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
783   } else {
784     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
785 
786     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
787     switch (cs) {
788     case CS_CARTESIAN:
789       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
790       break;
791     case CS_POLAR:
792       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
793       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
794       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
795       break;
796     case CS_CYLINDRICAL:
797       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       trcoords[2] = coords[2];
801       break;
802     case CS_SPHERICAL:
803       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
804       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
805       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
806       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
807       break;
808     }
809     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
810   }
811   PetscFunctionReturn(PETSC_SUCCESS);
812 }
813 
814 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
815 {
816   DM_Plex          *mesh = (DM_Plex *)dm->data;
817   DM                cdm, cdmCell;
818   PetscSection      coordSection, coordSectionCell;
819   Vec               coordinates, coordinatesCell;
820   PetscViewerFormat format;
821 
822   PetscFunctionBegin;
823   PetscCall(PetscViewerGetFormat(viewer, &format));
824   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
825     const char *name;
826     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
827     PetscInt    pStart, pEnd, p, numLabels, l;
828     PetscMPIInt rank, size;
829 
830     PetscCall(DMGetCoordinateDM(dm, &cdm));
831     PetscCall(DMGetCoordinateSection(dm, &coordSection));
832     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
833     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
834     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
835     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
836     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
837     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
838     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
839     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
840     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
841     PetscCall(DMGetDimension(dm, &dim));
842     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
843     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
844     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
845     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
847     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
848     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
849     for (p = pStart; p < pEnd; ++p) {
850       PetscInt dof, off, s;
851 
852       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
853       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
854       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
855     }
856     PetscCall(PetscViewerFlush(viewer));
857     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
858     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
859     for (p = pStart; p < pEnd; ++p) {
860       PetscInt dof, off, c;
861 
862       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
863       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
864       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
865     }
866     PetscCall(PetscViewerFlush(viewer));
867     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
868     if (coordSection && coordinates) {
869       CoordSystem        cs = CS_CARTESIAN;
870       const PetscScalar *array, *arrayCell = NULL;
871       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
872       PetscMPIInt        rank;
873       const char        *name;
874 
875       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
876       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
877       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
878       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
879       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
880       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
881       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
882       pStart = PetscMin(pvStart, pcStart);
883       pEnd   = PetscMax(pvEnd, pcEnd);
884       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
885       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
886       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
887       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
888 
889       PetscCall(VecGetArrayRead(coordinates, &array));
890       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
891       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
892       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
893       for (p = pStart; p < pEnd; ++p) {
894         PetscInt dof, off;
895 
896         if (p >= pvStart && p < pvEnd) {
897           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
898           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
899           if (dof) {
900             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
901             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
902             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
903           }
904         }
905         if (cdmCell && p >= pcStart && p < pcEnd) {
906           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
907           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
908           if (dof) {
909             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
910             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
911             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
912           }
913         }
914       }
915       PetscCall(PetscViewerFlush(viewer));
916       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
917       PetscCall(VecRestoreArrayRead(coordinates, &array));
918       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
919     }
920     PetscCall(DMGetNumLabels(dm, &numLabels));
921     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
922     for (l = 0; l < numLabels; ++l) {
923       DMLabel     label;
924       PetscBool   isdepth;
925       const char *name;
926 
927       PetscCall(DMGetLabelName(dm, l, &name));
928       PetscCall(PetscStrcmp(name, "depth", &isdepth));
929       if (isdepth) continue;
930       PetscCall(DMGetLabel(dm, name, &label));
931       PetscCall(DMLabelView(label, viewer));
932     }
933     if (size > 1) {
934       PetscSF sf;
935 
936       PetscCall(DMGetPointSF(dm, &sf));
937       PetscCall(PetscSFView(sf, viewer));
938     }
939     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
940     PetscCall(PetscViewerFlush(viewer));
941   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
942     const char  *name, *color;
943     const char  *defcolors[3]  = {"gray", "orange", "green"};
944     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
945     char         lname[PETSC_MAX_PATH_LEN];
946     PetscReal    scale      = 2.0;
947     PetscReal    tikzscale  = 1.0;
948     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
949     double       tcoords[3];
950     PetscScalar *coords;
951     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
952     PetscMPIInt  rank, size;
953     char       **names, **colors, **lcolors;
954     PetscBool    flg, lflg;
955     PetscBT      wp = NULL;
956     PetscInt     pEnd, pStart;
957 
958     PetscCall(DMGetCoordinateDM(dm, &cdm));
959     PetscCall(DMGetCoordinateSection(dm, &coordSection));
960     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
961     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
962     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
963     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
964     PetscCall(DMGetDimension(dm, &dim));
965     PetscCall(DMPlexGetDepth(dm, &depth));
966     PetscCall(DMGetNumLabels(dm, &numLabels));
967     numLabels  = PetscMax(numLabels, 10);
968     numColors  = 10;
969     numLColors = 10;
970     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
971     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
972     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
973     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
974     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
975     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
976     n = 4;
977     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
978     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
979     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
980     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
981     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
982     if (!useLabels) numLabels = 0;
983     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
984     if (!useColors) {
985       numColors = 3;
986       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
987     }
988     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
989     if (!useColors) {
990       numLColors = 4;
991       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
992     }
993     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
994     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
995     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
996     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
997     if (depth < dim) plotEdges = PETSC_FALSE;
998     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
999 
1000     /* filter points with labelvalue != labeldefaultvalue */
1001     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1002     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1003     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1004     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1005     if (lflg) {
1006       DMLabel lbl;
1007 
1008       PetscCall(DMGetLabel(dm, lname, &lbl));
1009       if (lbl) {
1010         PetscInt val, defval;
1011 
1012         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1013         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1014         for (c = pStart; c < pEnd; c++) {
1015           PetscInt *closure = NULL;
1016           PetscInt  closureSize;
1017 
1018           PetscCall(DMLabelGetValue(lbl, c, &val));
1019           if (val == defval) continue;
1020 
1021           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1022           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1023           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1024         }
1025       }
1026     }
1027 
1028     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1029     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1030     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1031     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1032 \\documentclass[tikz]{standalone}\n\n\
1033 \\usepackage{pgflibraryshapes}\n\
1034 \\usetikzlibrary{backgrounds}\n\
1035 \\usetikzlibrary{arrows}\n\
1036 \\begin{document}\n"));
1037     if (size > 1) {
1038       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1039       for (p = 0; p < size; ++p) {
1040         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1041         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1042       }
1043       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1044     }
1045     if (drawHasse) {
1046       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1047 
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1060     }
1061     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1062 
1063     /* Plot vertices */
1064     PetscCall(VecGetArray(coordinates, &coords));
1065     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1066     for (v = vStart; v < vEnd; ++v) {
1067       PetscInt  off, dof, d;
1068       PetscBool isLabeled = PETSC_FALSE;
1069 
1070       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1071       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1072       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1073       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1074       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1075       for (d = 0; d < dof; ++d) {
1076         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1077         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1078       }
1079       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1080       if (dim == 3) {
1081         PetscReal tmp = tcoords[1];
1082         tcoords[1]    = tcoords[2];
1083         tcoords[2]    = -tmp;
1084       }
1085       for (d = 0; d < dof; ++d) {
1086         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1088       }
1089       if (drawHasse) color = colors[0 % numColors];
1090       else color = colors[rank % numColors];
1091       for (l = 0; l < numLabels; ++l) {
1092         PetscInt val;
1093         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1094         if (val >= 0) {
1095           color     = lcolors[l % numLColors];
1096           isLabeled = PETSC_TRUE;
1097           break;
1098         }
1099       }
1100       if (drawNumbers[0]) {
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1102       } else if (drawColors[0]) {
1103         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1104       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1105     }
1106     PetscCall(VecRestoreArray(coordinates, &coords));
1107     PetscCall(PetscViewerFlush(viewer));
1108     /* Plot edges */
1109     if (plotEdges) {
1110       PetscCall(VecGetArray(coordinates, &coords));
1111       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1112       for (e = eStart; e < eEnd; ++e) {
1113         const PetscInt *cone;
1114         PetscInt        coneSize, offA, offB, dof, d;
1115 
1116         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1117         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1118         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1119         PetscCall(DMPlexGetCone(dm, e, &cone));
1120         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1121         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1122         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1123         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1124         for (d = 0; d < dof; ++d) {
1125           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1126           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1127         }
1128         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1129         if (dim == 3) {
1130           PetscReal tmp = tcoords[1];
1131           tcoords[1]    = tcoords[2];
1132           tcoords[2]    = -tmp;
1133         }
1134         for (d = 0; d < dof; ++d) {
1135           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1136           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1137         }
1138         if (drawHasse) color = colors[1 % numColors];
1139         else color = colors[rank % numColors];
1140         for (l = 0; l < numLabels; ++l) {
1141           PetscInt val;
1142           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1143           if (val >= 0) {
1144             color = lcolors[l % numLColors];
1145             break;
1146           }
1147         }
1148         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1149       }
1150       PetscCall(VecRestoreArray(coordinates, &coords));
1151       PetscCall(PetscViewerFlush(viewer));
1152       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1153     }
1154     /* Plot cells */
1155     if (dim == 3 || !drawNumbers[1]) {
1156       for (e = eStart; e < eEnd; ++e) {
1157         const PetscInt *cone;
1158 
1159         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1160         color = colors[rank % numColors];
1161         for (l = 0; l < numLabels; ++l) {
1162           PetscInt val;
1163           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1164           if (val >= 0) {
1165             color = lcolors[l % numLColors];
1166             break;
1167           }
1168         }
1169         PetscCall(DMPlexGetCone(dm, e, &cone));
1170         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1171       }
1172     } else {
1173       DMPolytopeType ct;
1174 
1175       /* Drawing a 2D polygon */
1176       for (c = cStart; c < cEnd; ++c) {
1177         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1178         PetscCall(DMPlexGetCellType(dm, c, &ct));
1179         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1180           const PetscInt *cone;
1181           PetscInt        coneSize, e;
1182 
1183           PetscCall(DMPlexGetCone(dm, c, &cone));
1184           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1185           for (e = 0; e < coneSize; ++e) {
1186             const PetscInt *econe;
1187 
1188             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1189             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1190           }
1191         } else {
1192           PetscInt *closure = NULL;
1193           PetscInt  closureSize, Nv = 0, v;
1194 
1195           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1196           for (p = 0; p < closureSize * 2; p += 2) {
1197             const PetscInt point = closure[p];
1198 
1199             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1200           }
1201           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1202           for (v = 0; v <= Nv; ++v) {
1203             const PetscInt vertex = closure[v % Nv];
1204 
1205             if (v > 0) {
1206               if (plotEdges) {
1207                 const PetscInt *edge;
1208                 PetscInt        endpoints[2], ne;
1209 
1210                 endpoints[0] = closure[v - 1];
1211                 endpoints[1] = vertex;
1212                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1213                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1214                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1215                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1216               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1217             }
1218             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1219           }
1220           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1221           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1222         }
1223       }
1224     }
1225     for (c = cStart; c < cEnd; ++c) {
1226       double             ccoords[3] = {0.0, 0.0, 0.0};
1227       PetscBool          isLabeled  = PETSC_FALSE;
1228       PetscScalar       *cellCoords = NULL;
1229       const PetscScalar *array;
1230       PetscInt           numCoords, cdim, d;
1231       PetscBool          isDG;
1232 
1233       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1234       PetscCall(DMGetCoordinateDim(dm, &cdim));
1235       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1236       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1237       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1238       for (p = 0; p < numCoords / cdim; ++p) {
1239         for (d = 0; d < cdim; ++d) {
1240           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1241           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1242         }
1243         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1244         if (cdim == 3) {
1245           PetscReal tmp = tcoords[1];
1246           tcoords[1]    = tcoords[2];
1247           tcoords[2]    = -tmp;
1248         }
1249         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1250       }
1251       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1252       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1253       for (d = 0; d < cdim; ++d) {
1254         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1255         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1256       }
1257       if (drawHasse) color = colors[depth % numColors];
1258       else color = colors[rank % numColors];
1259       for (l = 0; l < numLabels; ++l) {
1260         PetscInt val;
1261         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1262         if (val >= 0) {
1263           color     = lcolors[l % numLColors];
1264           isLabeled = PETSC_TRUE;
1265           break;
1266         }
1267       }
1268       if (drawNumbers[dim]) {
1269         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1270       } else if (drawColors[dim]) {
1271         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1272       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1273     }
1274     if (drawHasse) {
1275       color = colors[depth % numColors];
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1277       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1279       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1281 
1282       color = colors[1 % numColors];
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1286       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1288 
1289       color = colors[0 % numColors];
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1295 
1296       for (p = pStart; p < pEnd; ++p) {
1297         const PetscInt *cone;
1298         PetscInt        coneSize, cp;
1299 
1300         PetscCall(DMPlexGetCone(dm, p, &cone));
1301         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1302         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1303       }
1304     }
1305     PetscCall(PetscViewerFlush(viewer));
1306     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1307     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1308     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1309     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1310     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1311     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1312     PetscCall(PetscFree3(names, colors, lcolors));
1313     PetscCall(PetscBTDestroy(&wp));
1314   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1315     Vec                    cown, acown;
1316     VecScatter             sct;
1317     ISLocalToGlobalMapping g2l;
1318     IS                     gid, acis;
1319     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1320     MPI_Group              ggroup, ngroup;
1321     PetscScalar           *array, nid;
1322     const PetscInt        *idxs;
1323     PetscInt              *idxs2, *start, *adjacency, *work;
1324     PetscInt64             lm[3], gm[3];
1325     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1326     PetscMPIInt            d1, d2, rank;
1327 
1328     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1329     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1330 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1331     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1332 #endif
1333     if (ncomm != MPI_COMM_NULL) {
1334       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1335       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1336       d1 = 0;
1337       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1338       nid = d2;
1339       PetscCallMPI(MPI_Group_free(&ggroup));
1340       PetscCallMPI(MPI_Group_free(&ngroup));
1341       PetscCallMPI(MPI_Comm_free(&ncomm));
1342     } else nid = 0.0;
1343 
1344     /* Get connectivity */
1345     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1346     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1347 
1348     /* filter overlapped local cells */
1349     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1350     PetscCall(ISGetIndices(gid, &idxs));
1351     PetscCall(ISGetLocalSize(gid, &cum));
1352     PetscCall(PetscMalloc1(cum, &idxs2));
1353     for (c = cStart, cum = 0; c < cEnd; c++) {
1354       if (idxs[c - cStart] < 0) continue;
1355       idxs2[cum++] = idxs[c - cStart];
1356     }
1357     PetscCall(ISRestoreIndices(gid, &idxs));
1358     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1359     PetscCall(ISDestroy(&gid));
1360     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1361 
1362     /* support for node-aware cell locality */
1363     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1364     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1365     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1366     PetscCall(VecGetArray(cown, &array));
1367     for (c = 0; c < numVertices; c++) array[c] = nid;
1368     PetscCall(VecRestoreArray(cown, &array));
1369     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1370     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1371     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1372     PetscCall(ISDestroy(&acis));
1373     PetscCall(VecScatterDestroy(&sct));
1374     PetscCall(VecDestroy(&cown));
1375 
1376     /* compute edgeCut */
1377     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1378     PetscCall(PetscMalloc1(cum, &work));
1379     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1380     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1381     PetscCall(ISDestroy(&gid));
1382     PetscCall(VecGetArray(acown, &array));
1383     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1384       PetscInt totl;
1385 
1386       totl = start[c + 1] - start[c];
1387       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1388       for (i = 0; i < totl; i++) {
1389         if (work[i] < 0) {
1390           ect += 1;
1391           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1392         }
1393       }
1394     }
1395     PetscCall(PetscFree(work));
1396     PetscCall(VecRestoreArray(acown, &array));
1397     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1398     lm[1] = -numVertices;
1399     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1400     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1401     lm[0] = ect;                     /* edgeCut */
1402     lm[1] = ectn;                    /* node-aware edgeCut */
1403     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1404     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1405     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1406 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1407     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1408 #else
1409     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1410 #endif
1411     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1412     PetscCall(PetscFree(start));
1413     PetscCall(PetscFree(adjacency));
1414     PetscCall(VecDestroy(&acown));
1415   } else {
1416     const char    *name;
1417     PetscInt      *sizes, *hybsizes, *ghostsizes;
1418     PetscInt       locDepth, depth, cellHeight, dim, d;
1419     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1420     PetscInt       numLabels, l, maxSize = 17;
1421     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1422     MPI_Comm       comm;
1423     PetscMPIInt    size, rank;
1424 
1425     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1426     PetscCallMPI(MPI_Comm_size(comm, &size));
1427     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1428     PetscCall(DMGetDimension(dm, &dim));
1429     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1430     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1431     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1432     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1433     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1434     PetscCall(DMPlexGetDepth(dm, &locDepth));
1435     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1436     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1437     gcNum = gcEnd - gcStart;
1438     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1439     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1440     for (d = 0; d <= depth; d++) {
1441       PetscInt Nc[2] = {0, 0}, ict;
1442 
1443       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1444       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1445       ict = ct0;
1446       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1447       ct0 = (DMPolytopeType)ict;
1448       for (p = pStart; p < pEnd; ++p) {
1449         DMPolytopeType ct;
1450 
1451         PetscCall(DMPlexGetCellType(dm, p, &ct));
1452         if (ct == ct0) ++Nc[0];
1453         else ++Nc[1];
1454       }
1455       if (size < maxSize) {
1456         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1457         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1458         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1459         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1460         for (p = 0; p < size; ++p) {
1461           if (rank == 0) {
1462             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1463             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1464             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1465           }
1466         }
1467       } else {
1468         PetscInt locMinMax[2];
1469 
1470         locMinMax[0] = Nc[0] + Nc[1];
1471         locMinMax[1] = Nc[0] + Nc[1];
1472         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1473         locMinMax[0] = Nc[1];
1474         locMinMax[1] = Nc[1];
1475         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1476         if (d == depth) {
1477           locMinMax[0] = gcNum;
1478           locMinMax[1] = gcNum;
1479           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1480         }
1481         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1482         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1483         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1484         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1485       }
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1487     }
1488     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1489     {
1490       const PetscReal *maxCell;
1491       const PetscReal *L;
1492       PetscBool        localized;
1493 
1494       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1495       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1496       if (L || localized) {
1497         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1498         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1499         if (L) {
1500           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1501           for (d = 0; d < dim; ++d) {
1502             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1503             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1504           }
1505           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1506         }
1507         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1508         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1509       }
1510     }
1511     PetscCall(DMGetNumLabels(dm, &numLabels));
1512     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1513     for (l = 0; l < numLabels; ++l) {
1514       DMLabel         label;
1515       const char     *name;
1516       IS              valueIS;
1517       const PetscInt *values;
1518       PetscInt        numValues, v;
1519 
1520       PetscCall(DMGetLabelName(dm, l, &name));
1521       PetscCall(DMGetLabel(dm, name, &label));
1522       PetscCall(DMLabelGetNumValues(label, &numValues));
1523       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1524       PetscCall(DMLabelGetValueIS(label, &valueIS));
1525       PetscCall(ISGetIndices(valueIS, &values));
1526       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1527       for (v = 0; v < numValues; ++v) {
1528         PetscInt size;
1529 
1530         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1531         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1532         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1533       }
1534       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1535       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1536       PetscCall(ISRestoreIndices(valueIS, &values));
1537       PetscCall(ISDestroy(&valueIS));
1538     }
1539     {
1540       char    **labelNames;
1541       PetscInt  Nl = numLabels;
1542       PetscBool flg;
1543 
1544       PetscCall(PetscMalloc1(Nl, &labelNames));
1545       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1546       for (l = 0; l < Nl; ++l) {
1547         DMLabel label;
1548 
1549         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1550         if (flg) {
1551           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1552           PetscCall(DMLabelView(label, viewer));
1553         }
1554         PetscCall(PetscFree(labelNames[l]));
1555       }
1556       PetscCall(PetscFree(labelNames));
1557     }
1558     /* If no fields are specified, people do not want to see adjacency */
1559     if (dm->Nf) {
1560       PetscInt f;
1561 
1562       for (f = 0; f < dm->Nf; ++f) {
1563         const char *name;
1564 
1565         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1566         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1567         PetscCall(PetscViewerASCIIPushTab(viewer));
1568         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1569         if (dm->fields[f].adjacency[0]) {
1570           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1571           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1572         } else {
1573           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1574           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1575         }
1576         PetscCall(PetscViewerASCIIPopTab(viewer));
1577       }
1578     }
1579     PetscCall(DMGetCoarseDM(dm, &cdm));
1580     if (cdm) {
1581       PetscCall(PetscViewerASCIIPushTab(viewer));
1582       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1583       PetscCall(DMPlexView_Ascii(cdm, viewer));
1584       PetscCall(PetscViewerASCIIPopTab(viewer));
1585     }
1586   }
1587   PetscFunctionReturn(PETSC_SUCCESS);
1588 }
1589 
1590 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1591 {
1592   DMPolytopeType ct;
1593   PetscMPIInt    rank;
1594   PetscInt       cdim;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   PetscCall(DMGetCoordinateDim(dm, &cdim));
1600   switch (ct) {
1601   case DM_POLYTOPE_SEGMENT:
1602   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1603     switch (cdim) {
1604     case 1: {
1605       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1606       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1607 
1608       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1609       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1610       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1611     } break;
1612     case 2: {
1613       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1614       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1615       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1616 
1617       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1618       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1619       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1620     } break;
1621     default:
1622       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1623     }
1624     break;
1625   case DM_POLYTOPE_TRIANGLE:
1626     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1627     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1628     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1629     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1630     break;
1631   case DM_POLYTOPE_QUADRILATERAL:
1632     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1633     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1635     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1636     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1637     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1638     break;
1639   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1640     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1641     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1642     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1643     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1644     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1645     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1646     break;
1647   case DM_POLYTOPE_FV_GHOST:
1648     break;
1649   default:
1650     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1651   }
1652   PetscFunctionReturn(PETSC_SUCCESS);
1653 }
1654 
1655 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1656 {
1657   DMPolytopeType ct;
1658   PetscReal      centroid[2] = {0., 0.};
1659   PetscMPIInt    rank;
1660   PetscInt       fillColor, v, e, d;
1661 
1662   PetscFunctionBegin;
1663   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1664   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1665   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1666   switch (ct) {
1667   case DM_POLYTOPE_TRIANGLE: {
1668     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1669 
1670     for (v = 0; v < 3; ++v) {
1671       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1672       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1673     }
1674     for (e = 0; e < 3; ++e) {
1675       refCoords[0] = refVertices[e * 2 + 0];
1676       refCoords[1] = refVertices[e * 2 + 1];
1677       for (d = 1; d <= edgeDiv; ++d) {
1678         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1679         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1680       }
1681       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1682       for (d = 0; d < edgeDiv; ++d) {
1683         PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1684         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1685       }
1686     }
1687   } break;
1688   default:
1689     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1690   }
1691   PetscFunctionReturn(PETSC_SUCCESS);
1692 }
1693 
1694 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1695 {
1696   PetscDraw    draw;
1697   DM           cdm;
1698   PetscSection coordSection;
1699   Vec          coordinates;
1700   PetscReal    xyl[3], xyr[3];
1701   PetscReal   *refCoords, *edgeCoords;
1702   PetscBool    isnull, drawAffine = PETSC_TRUE;
1703   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1704 
1705   PetscFunctionBegin;
1706   PetscCall(DMGetCoordinateDim(dm, &dim));
1707   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1708   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1709   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1710   PetscCall(DMGetCoordinateDM(dm, &cdm));
1711   PetscCall(DMGetLocalSection(cdm, &coordSection));
1712   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1713   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1714   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1715 
1716   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1717   PetscCall(PetscDrawIsNull(draw, &isnull));
1718   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1719   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1720 
1721   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1722   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1723   PetscCall(PetscDrawClear(draw));
1724 
1725   for (c = cStart; c < cEnd; ++c) {
1726     PetscScalar       *coords = NULL;
1727     const PetscScalar *coords_arr;
1728     PetscInt           numCoords;
1729     PetscBool          isDG;
1730 
1731     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1732     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1733     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1734     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1735   }
1736   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1737   PetscCall(PetscDrawFlush(draw));
1738   PetscCall(PetscDrawPause(draw));
1739   PetscCall(PetscDrawSave(draw));
1740   PetscFunctionReturn(PETSC_SUCCESS);
1741 }
1742 
1743 #if defined(PETSC_HAVE_EXODUSII)
1744   #include <exodusII.h>
1745   #include <petscviewerexodusii.h>
1746 #endif
1747 
1748 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1749 {
1750   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1751   char      name[PETSC_MAX_PATH_LEN];
1752 
1753   PetscFunctionBegin;
1754   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1755   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1756   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1757   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1759   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1761   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1762   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1763   if (iascii) {
1764     PetscViewerFormat format;
1765     PetscCall(PetscViewerGetFormat(viewer, &format));
1766     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1767     else PetscCall(DMPlexView_Ascii(dm, viewer));
1768   } else if (ishdf5) {
1769 #if defined(PETSC_HAVE_HDF5)
1770     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1771 #else
1772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1773 #endif
1774   } else if (isvtk) {
1775     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1776   } else if (isdraw) {
1777     PetscCall(DMPlexView_Draw(dm, viewer));
1778   } else if (isglvis) {
1779     PetscCall(DMPlexView_GLVis(dm, viewer));
1780 #if defined(PETSC_HAVE_EXODUSII)
1781   } else if (isexodus) {
1782     /*
1783       exodusII requires that all sets be part of exactly one cell set.
1784       If the dm does not have a "Cell Sets" label defined, we create one
1785       with ID 1, containing all cells.
1786       Note that if the Cell Sets label is defined but does not cover all cells,
1787       we may still have a problem. This should probably be checked here or in the viewer;
1788     */
1789     PetscInt numCS;
1790     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1791     if (!numCS) {
1792       PetscInt cStart, cEnd, c;
1793       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1794       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1795       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1796     }
1797     PetscCall(DMView_PlexExodusII(dm, viewer));
1798 #endif
1799 #if defined(PETSC_HAVE_CGNS)
1800   } else if (iscgns) {
1801     PetscCall(DMView_PlexCGNS(dm, viewer));
1802 #endif
1803   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1804   /* Optionally view the partition */
1805   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1806   if (flg) {
1807     Vec ranks;
1808     PetscCall(DMPlexCreateRankField(dm, &ranks));
1809     PetscCall(VecView(ranks, viewer));
1810     PetscCall(VecDestroy(&ranks));
1811   }
1812   /* Optionally view a label */
1813   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1814   if (flg) {
1815     DMLabel label;
1816     Vec     val;
1817 
1818     PetscCall(DMGetLabel(dm, name, &label));
1819     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1820     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1821     PetscCall(VecView(val, viewer));
1822     PetscCall(VecDestroy(&val));
1823   }
1824   PetscFunctionReturn(PETSC_SUCCESS);
1825 }
1826 
1827 /*@
1828   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1829 
1830   Collective
1831 
1832   Input Parameters:
1833 + dm     - The `DM` whose topology is to be saved
1834 - viewer - The `PetscViewer` to save it in
1835 
1836   Level: advanced
1837 
1838 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1839 @*/
1840 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1841 {
1842   PetscBool ishdf5;
1843 
1844   PetscFunctionBegin;
1845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1846   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1847   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1848   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1849   if (ishdf5) {
1850 #if defined(PETSC_HAVE_HDF5)
1851     PetscViewerFormat format;
1852     PetscCall(PetscViewerGetFormat(viewer, &format));
1853     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1854       IS globalPointNumbering;
1855 
1856       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1857       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1858       PetscCall(ISDestroy(&globalPointNumbering));
1859     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1860 #else
1861     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1862 #endif
1863   }
1864   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 /*@
1869   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1870 
1871   Collective
1872 
1873   Input Parameters:
1874 + dm     - The `DM` whose coordinates are to be saved
1875 - viewer - The `PetscViewer` for saving
1876 
1877   Level: advanced
1878 
1879 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1880 @*/
1881 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1882 {
1883   PetscBool ishdf5;
1884 
1885   PetscFunctionBegin;
1886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1887   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1888   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1889   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1890   if (ishdf5) {
1891 #if defined(PETSC_HAVE_HDF5)
1892     PetscViewerFormat format;
1893     PetscCall(PetscViewerGetFormat(viewer, &format));
1894     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1895       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1896     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1897 #else
1898     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1899 #endif
1900   }
1901   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1902   PetscFunctionReturn(PETSC_SUCCESS);
1903 }
1904 
1905 /*@
1906   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1907 
1908   Collective
1909 
1910   Input Parameters:
1911 + dm     - The `DM` whose labels are to be saved
1912 - viewer - The `PetscViewer` for saving
1913 
1914   Level: advanced
1915 
1916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1917 @*/
1918 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1919 {
1920   PetscBool ishdf5;
1921 
1922   PetscFunctionBegin;
1923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1924   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1925   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1926   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1927   if (ishdf5) {
1928 #if defined(PETSC_HAVE_HDF5)
1929     IS                globalPointNumbering;
1930     PetscViewerFormat format;
1931 
1932     PetscCall(PetscViewerGetFormat(viewer, &format));
1933     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1934       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1935       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1936       PetscCall(ISDestroy(&globalPointNumbering));
1937     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1938 #else
1939     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1940 #endif
1941   }
1942   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1943   PetscFunctionReturn(PETSC_SUCCESS);
1944 }
1945 
1946 /*@
1947   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1948 
1949   Collective
1950 
1951   Input Parameters:
1952 + dm        - The `DM` that contains the topology on which the section to be saved is defined
1953 . viewer    - The `PetscViewer` for saving
1954 - sectiondm - The `DM` that contains the section to be saved
1955 
1956   Level: advanced
1957 
1958   Notes:
1959   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
1960 
1961   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1962 
1963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1964 @*/
1965 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1966 {
1967   PetscBool ishdf5;
1968 
1969   PetscFunctionBegin;
1970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1971   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1972   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1973   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1974   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1975   if (ishdf5) {
1976 #if defined(PETSC_HAVE_HDF5)
1977     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1978 #else
1979     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1980 #endif
1981   }
1982   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1983   PetscFunctionReturn(PETSC_SUCCESS);
1984 }
1985 
1986 /*@
1987   DMPlexGlobalVectorView - Saves a global vector
1988 
1989   Collective
1990 
1991   Input Parameters:
1992 + dm        - The `DM` that represents the topology
1993 . viewer    - The `PetscViewer` to save data with
1994 . sectiondm - The `DM` that contains the global section on which vec is defined
1995 - vec       - The global vector to be saved
1996 
1997   Level: advanced
1998 
1999   Notes:
2000   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2001 
2002   Calling sequence:
2003 .vb
2004        DMCreate(PETSC_COMM_WORLD, &dm);
2005        DMSetType(dm, DMPLEX);
2006        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2007        DMClone(dm, &sectiondm);
2008        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2009        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2010        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2011        PetscSectionSetChart(section, pStart, pEnd);
2012        PetscSectionSetUp(section);
2013        DMSetLocalSection(sectiondm, section);
2014        PetscSectionDestroy(&section);
2015        DMGetGlobalVector(sectiondm, &vec);
2016        PetscObjectSetName((PetscObject)vec, "vec_name");
2017        DMPlexTopologyView(dm, viewer);
2018        DMPlexSectionView(dm, viewer, sectiondm);
2019        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2020        DMRestoreGlobalVector(sectiondm, &vec);
2021        DMDestroy(&sectiondm);
2022        DMDestroy(&dm);
2023 .ve
2024 
2025 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2026 @*/
2027 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2028 {
2029   PetscBool ishdf5;
2030 
2031   PetscFunctionBegin;
2032   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2033   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2034   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2035   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2036   /* Check consistency */
2037   {
2038     PetscSection section;
2039     PetscBool    includesConstraints;
2040     PetscInt     m, m1;
2041 
2042     PetscCall(VecGetLocalSize(vec, &m1));
2043     PetscCall(DMGetGlobalSection(sectiondm, &section));
2044     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2045     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2046     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2047     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2048   }
2049   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2050   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2051   if (ishdf5) {
2052 #if defined(PETSC_HAVE_HDF5)
2053     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2054 #else
2055     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2056 #endif
2057   }
2058   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2059   PetscFunctionReturn(PETSC_SUCCESS);
2060 }
2061 
2062 /*@
2063   DMPlexLocalVectorView - Saves a local vector
2064 
2065   Collective
2066 
2067   Input Parameters:
2068 + dm        - The `DM` that represents the topology
2069 . viewer    - The `PetscViewer` to save data with
2070 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2071 - vec       - The local vector to be saved
2072 
2073   Level: advanced
2074 
2075   Note:
2076   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2077 
2078   Calling sequence:
2079 .vb
2080        DMCreate(PETSC_COMM_WORLD, &dm);
2081        DMSetType(dm, DMPLEX);
2082        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2083        DMClone(dm, &sectiondm);
2084        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2085        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2086        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2087        PetscSectionSetChart(section, pStart, pEnd);
2088        PetscSectionSetUp(section);
2089        DMSetLocalSection(sectiondm, section);
2090        DMGetLocalVector(sectiondm, &vec);
2091        PetscObjectSetName((PetscObject)vec, "vec_name");
2092        DMPlexTopologyView(dm, viewer);
2093        DMPlexSectionView(dm, viewer, sectiondm);
2094        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2095        DMRestoreLocalVector(sectiondm, &vec);
2096        DMDestroy(&sectiondm);
2097        DMDestroy(&dm);
2098 .ve
2099 
2100 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2101 @*/
2102 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2103 {
2104   PetscBool ishdf5;
2105 
2106   PetscFunctionBegin;
2107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2108   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2109   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2110   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2111   /* Check consistency */
2112   {
2113     PetscSection section;
2114     PetscBool    includesConstraints;
2115     PetscInt     m, m1;
2116 
2117     PetscCall(VecGetLocalSize(vec, &m1));
2118     PetscCall(DMGetLocalSection(sectiondm, &section));
2119     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2120     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2121     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2122     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2123   }
2124   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2125   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2126   if (ishdf5) {
2127 #if defined(PETSC_HAVE_HDF5)
2128     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2129 #else
2130     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2131 #endif
2132   }
2133   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2134   PetscFunctionReturn(PETSC_SUCCESS);
2135 }
2136 
2137 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2138 {
2139   PetscBool ishdf5;
2140 
2141   PetscFunctionBegin;
2142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2143   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2144   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2145   if (ishdf5) {
2146 #if defined(PETSC_HAVE_HDF5)
2147     PetscViewerFormat format;
2148     PetscCall(PetscViewerGetFormat(viewer, &format));
2149     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2150       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2151     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2152       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2153     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2154     PetscFunctionReturn(PETSC_SUCCESS);
2155 #else
2156     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2157 #endif
2158   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2159 }
2160 
2161 /*@
2162   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2163 
2164   Collective
2165 
2166   Input Parameters:
2167 + dm     - The `DM` into which the topology is loaded
2168 - viewer - The `PetscViewer` for the saved topology
2169 
2170   Output Parameter:
2171 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2172 
2173   Level: advanced
2174 
2175 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2176           `PetscViewer`, `PetscSF`
2177 @*/
2178 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2179 {
2180   PetscBool ishdf5;
2181 
2182   PetscFunctionBegin;
2183   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2184   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2185   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2186   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2187   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2188   if (ishdf5) {
2189 #if defined(PETSC_HAVE_HDF5)
2190     PetscViewerFormat format;
2191     PetscCall(PetscViewerGetFormat(viewer, &format));
2192     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2193       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2194     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2195 #else
2196     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2197 #endif
2198   }
2199   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2200   PetscFunctionReturn(PETSC_SUCCESS);
2201 }
2202 
2203 /*@
2204   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2205 
2206   Collective
2207 
2208   Input Parameters:
2209 + dm                   - The `DM` into which the coordinates are loaded
2210 . viewer               - The `PetscViewer` for the saved coordinates
2211 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2212 
2213   Level: advanced
2214 
2215 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2216           `PetscSF`, `PetscViewer`
2217 @*/
2218 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2219 {
2220   PetscBool ishdf5;
2221 
2222   PetscFunctionBegin;
2223   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2224   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2225   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2226   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2227   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2228   if (ishdf5) {
2229 #if defined(PETSC_HAVE_HDF5)
2230     PetscViewerFormat format;
2231     PetscCall(PetscViewerGetFormat(viewer, &format));
2232     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2233       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2234     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2235 #else
2236     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2237 #endif
2238   }
2239   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2240   PetscFunctionReturn(PETSC_SUCCESS);
2241 }
2242 
2243 /*@
2244   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2245 
2246   Collective
2247 
2248   Input Parameters:
2249 + dm                   - The `DM` into which the labels are loaded
2250 . viewer               - The `PetscViewer` for the saved labels
2251 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2252 
2253   Level: advanced
2254 
2255   Note:
2256   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2257 
2258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2259           `PetscSF`, `PetscViewer`
2260 @*/
2261 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2262 {
2263   PetscBool ishdf5;
2264 
2265   PetscFunctionBegin;
2266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2267   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2268   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2269   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2270   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2271   if (ishdf5) {
2272 #if defined(PETSC_HAVE_HDF5)
2273     PetscViewerFormat format;
2274 
2275     PetscCall(PetscViewerGetFormat(viewer, &format));
2276     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2277       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2278     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2279 #else
2280     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2281 #endif
2282   }
2283   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2284   PetscFunctionReturn(PETSC_SUCCESS);
2285 }
2286 
2287 /*@
2288   DMPlexSectionLoad - Loads section into a `DMPLEX`
2289 
2290   Collective
2291 
2292   Input Parameters:
2293 + dm                   - The `DM` that represents the topology
2294 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2295 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated
2296 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2297 
2298   Output Parameters
2299 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2300 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2301 
2302   Level: advanced
2303 
2304   Notes:
2305   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2306 
2307   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2308 
2309   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2310 
2311   Example using 2 processes:
2312 .vb
2313   NX (number of points on dm): 4
2314   sectionA                   : the on-disk section
2315   vecA                       : a vector associated with sectionA
2316   sectionB                   : sectiondm's local section constructed in this function
2317   vecB (local)               : a vector associated with sectiondm's local section
2318   vecB (global)              : a vector associated with sectiondm's global section
2319 
2320                                      rank 0    rank 1
2321   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2322   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2323   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2324   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2325   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2326   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2327   sectionB->atlasDof             :     1 0 1 | 1 3
2328   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2329   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2330   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2331 .ve
2332   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2333 
2334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2335 @*/
2336 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2344   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2345   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2346   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2347   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2348   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2349   if (ishdf5) {
2350 #if defined(PETSC_HAVE_HDF5)
2351     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2352 #else
2353     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2354 #endif
2355   }
2356   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2357   PetscFunctionReturn(PETSC_SUCCESS);
2358 }
2359 
2360 /*@
2361   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2362 
2363   Collective
2364 
2365   Input Parameters:
2366 + dm        - The `DM` that represents the topology
2367 . viewer    - The `PetscViewer` that represents the on-disk vector data
2368 . sectiondm - The `DM` that contains the global section on which vec is defined
2369 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2370 - vec       - The global vector to set values of
2371 
2372   Level: advanced
2373 
2374   Notes:
2375   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2376 
2377   Calling sequence:
2378 .vb
2379        DMCreate(PETSC_COMM_WORLD, &dm);
2380        DMSetType(dm, DMPLEX);
2381        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2382        DMPlexTopologyLoad(dm, viewer, &sfX);
2383        DMClone(dm, &sectiondm);
2384        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2385        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2386        DMGetGlobalVector(sectiondm, &vec);
2387        PetscObjectSetName((PetscObject)vec, "vec_name");
2388        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2389        DMRestoreGlobalVector(sectiondm, &vec);
2390        PetscSFDestroy(&gsf);
2391        PetscSFDestroy(&sfX);
2392        DMDestroy(&sectiondm);
2393        DMDestroy(&dm);
2394 .ve
2395 
2396 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2397           `PetscSF`, `PetscViewer`
2398 @*/
2399 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2400 {
2401   PetscBool ishdf5;
2402 
2403   PetscFunctionBegin;
2404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2405   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2406   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2407   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2408   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2409   /* Check consistency */
2410   {
2411     PetscSection section;
2412     PetscBool    includesConstraints;
2413     PetscInt     m, m1;
2414 
2415     PetscCall(VecGetLocalSize(vec, &m1));
2416     PetscCall(DMGetGlobalSection(sectiondm, &section));
2417     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2418     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2419     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2420     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2421   }
2422   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2423   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2424   if (ishdf5) {
2425 #if defined(PETSC_HAVE_HDF5)
2426     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2427 #else
2428     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2429 #endif
2430   }
2431   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2432   PetscFunctionReturn(PETSC_SUCCESS);
2433 }
2434 
2435 /*@
2436   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2437 
2438   Collective
2439 
2440   Input Parameters:
2441 + dm        - The `DM` that represents the topology
2442 . viewer    - The `PetscViewer` that represents the on-disk vector data
2443 . sectiondm - The `DM` that contains the local section on which vec is defined
2444 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2445 - vec       - The local vector to set values of
2446 
2447   Level: advanced
2448 
2449   Notes:
2450   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2451 
2452   Calling sequence:
2453 .vb
2454        DMCreate(PETSC_COMM_WORLD, &dm);
2455        DMSetType(dm, DMPLEX);
2456        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2457        DMPlexTopologyLoad(dm, viewer, &sfX);
2458        DMClone(dm, &sectiondm);
2459        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2460        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2461        DMGetLocalVector(sectiondm, &vec);
2462        PetscObjectSetName((PetscObject)vec, "vec_name");
2463        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2464        DMRestoreLocalVector(sectiondm, &vec);
2465        PetscSFDestroy(&lsf);
2466        PetscSFDestroy(&sfX);
2467        DMDestroy(&sectiondm);
2468        DMDestroy(&dm);
2469 .ve
2470 
2471 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2472           `PetscSF`, `PetscViewer`
2473 @*/
2474 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2475 {
2476   PetscBool ishdf5;
2477 
2478   PetscFunctionBegin;
2479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2480   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2481   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2482   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2483   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2484   /* Check consistency */
2485   {
2486     PetscSection section;
2487     PetscBool    includesConstraints;
2488     PetscInt     m, m1;
2489 
2490     PetscCall(VecGetLocalSize(vec, &m1));
2491     PetscCall(DMGetLocalSection(sectiondm, &section));
2492     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2493     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2494     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2495     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2496   }
2497   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2498   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2499   if (ishdf5) {
2500 #if defined(PETSC_HAVE_HDF5)
2501     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2502 #else
2503     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2504 #endif
2505   }
2506   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2507   PetscFunctionReturn(PETSC_SUCCESS);
2508 }
2509 
2510 PetscErrorCode DMDestroy_Plex(DM dm)
2511 {
2512   DM_Plex *mesh = (DM_Plex *)dm->data;
2513 
2514   PetscFunctionBegin;
2515   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2516   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2517   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2518   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2519   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2520   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2521   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2522   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2523   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2524   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2525   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2526   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2527   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2528   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2529   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2530   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2531   PetscCall(PetscFree(mesh->cones));
2532   PetscCall(PetscFree(mesh->coneOrientations));
2533   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2534   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2535   PetscCall(PetscFree(mesh->supports));
2536   PetscCall(PetscFree(mesh->cellTypes));
2537   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2538   PetscCall(PetscFree(mesh->tetgenOpts));
2539   PetscCall(PetscFree(mesh->triangleOpts));
2540   PetscCall(PetscFree(mesh->transformType));
2541   PetscCall(PetscFree(mesh->distributionName));
2542   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2543   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2544   PetscCall(ISDestroy(&mesh->subpointIS));
2545   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2546   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2547   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2548   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2549   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2550   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2551   PetscCall(ISDestroy(&mesh->anchorIS));
2552   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2553   PetscCall(PetscFree(mesh->parents));
2554   PetscCall(PetscFree(mesh->childIDs));
2555   PetscCall(PetscSectionDestroy(&mesh->childSection));
2556   PetscCall(PetscFree(mesh->children));
2557   PetscCall(DMDestroy(&mesh->referenceTree));
2558   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2559   PetscCall(PetscFree(mesh->neighbors));
2560   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2561   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2562   PetscCall(PetscFree(mesh));
2563   PetscFunctionReturn(PETSC_SUCCESS);
2564 }
2565 
2566 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2567 {
2568   PetscSection           sectionGlobal;
2569   PetscInt               bs = -1, mbs;
2570   PetscInt               localSize, localStart = 0;
2571   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2572   MatType                mtype;
2573   ISLocalToGlobalMapping ltog;
2574 
2575   PetscFunctionBegin;
2576   PetscCall(MatInitializePackage());
2577   mtype = dm->mattype;
2578   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2579   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2580   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2581   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2582   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2583   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2584   PetscCall(MatSetType(*J, mtype));
2585   PetscCall(MatSetFromOptions(*J));
2586   PetscCall(MatGetBlockSize(*J, &mbs));
2587   if (mbs > 1) bs = mbs;
2588   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2589   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2590   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2591   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2592   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2593   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2594   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2595   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2596   if (!isShell) {
2597     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2598     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2599     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2600 
2601     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2602 
2603     PetscCall(PetscCalloc1(localSize, &pblocks));
2604     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2605     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2606     for (p = pStart; p < pEnd; ++p) {
2607       switch (dm->blocking_type) {
2608       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2609         PetscInt bdof, offset;
2610 
2611         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2612         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2613         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2614         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2615         dof  = dof < 0 ? -(dof + 1) : dof;
2616         bdof = cdof && (dof - cdof) ? 1 : dof;
2617         if (dof) {
2618           if (bs < 0) {
2619             bs = bdof;
2620           } else if (bs != bdof) {
2621             bs = 1;
2622           }
2623         }
2624       } break;
2625       case DM_BLOCKING_FIELD_NODE: {
2626         for (PetscInt field = 0; field < num_fields; field++) {
2627           PetscInt num_comp, bdof, offset;
2628           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2629           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2630           if (dof < 0) continue;
2631           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2632           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2633           PetscAssert(dof % num_comp == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " field %" PetscInt_FMT " has %" PetscInt_FMT " dof, not divisible by %" PetscInt_FMT " component ", p, field, dof, num_comp);
2634           PetscInt num_nodes = dof / num_comp;
2635           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2636           // Handle possibly constant block size (unlikely)
2637           bdof = cdof && (dof - cdof) ? 1 : dof;
2638           if (dof) {
2639             if (bs < 0) {
2640               bs = bdof;
2641             } else if (bs != bdof) {
2642               bs = 1;
2643             }
2644           }
2645         }
2646       } break;
2647       }
2648     }
2649     /* Must have same blocksize on all procs (some might have no points) */
2650     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2651     bsLocal[1] = bs;
2652     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2653     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2654     else bs = bsMinMax[0];
2655     bs = PetscMax(1, bs);
2656     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2657     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2658       PetscCall(MatSetBlockSize(*J, bs));
2659       PetscCall(MatSetUp(*J));
2660     } else {
2661       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2662       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2663       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2664     }
2665     { // Consolidate blocks
2666       PetscInt nblocks = 0;
2667       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2668         if (pblocks[i] == 0) continue;
2669         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2670         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2671       }
2672       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2673     }
2674     PetscCall(PetscFree(pblocks));
2675   }
2676   PetscCall(MatSetDM(*J, dm));
2677   PetscFunctionReturn(PETSC_SUCCESS);
2678 }
2679 
2680 /*@
2681   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2682 
2683   Not Collective
2684 
2685   Input Parameter:
2686 . dm - The `DMPLEX`
2687 
2688   Output Parameter:
2689 . subsection - The subdomain section
2690 
2691   Level: developer
2692 
2693 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2694 @*/
2695 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2696 {
2697   DM_Plex *mesh = (DM_Plex *)dm->data;
2698 
2699   PetscFunctionBegin;
2700   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2701   if (!mesh->subdomainSection) {
2702     PetscSection section;
2703     PetscSF      sf;
2704 
2705     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2706     PetscCall(DMGetLocalSection(dm, &section));
2707     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2708     PetscCall(PetscSFDestroy(&sf));
2709   }
2710   *subsection = mesh->subdomainSection;
2711   PetscFunctionReturn(PETSC_SUCCESS);
2712 }
2713 
2714 /*@
2715   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2716 
2717   Not Collective
2718 
2719   Input Parameter:
2720 . dm - The `DMPLEX`
2721 
2722   Output Parameters:
2723 + pStart - The first mesh point
2724 - pEnd   - The upper bound for mesh points
2725 
2726   Level: beginner
2727 
2728 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2729 @*/
2730 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2731 {
2732   DM_Plex *mesh = (DM_Plex *)dm->data;
2733 
2734   PetscFunctionBegin;
2735   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2736   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2737   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2738   PetscFunctionReturn(PETSC_SUCCESS);
2739 }
2740 
2741 /*@
2742   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2743 
2744   Not Collective
2745 
2746   Input Parameters:
2747 + dm     - The `DMPLEX`
2748 . pStart - The first mesh point
2749 - pEnd   - The upper bound for mesh points
2750 
2751   Level: beginner
2752 
2753 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2754 @*/
2755 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2756 {
2757   DM_Plex *mesh = (DM_Plex *)dm->data;
2758 
2759   PetscFunctionBegin;
2760   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2761   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2762   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2763   PetscCall(PetscFree(mesh->cellTypes));
2764   PetscFunctionReturn(PETSC_SUCCESS);
2765 }
2766 
2767 /*@
2768   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2769 
2770   Not Collective
2771 
2772   Input Parameters:
2773 + dm - The `DMPLEX`
2774 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2775 
2776   Output Parameter:
2777 . size - The cone size for point `p`
2778 
2779   Level: beginner
2780 
2781 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2782 @*/
2783 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2784 {
2785   DM_Plex *mesh = (DM_Plex *)dm->data;
2786 
2787   PetscFunctionBegin;
2788   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2789   PetscAssertPointer(size, 3);
2790   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2791   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2792   PetscFunctionReturn(PETSC_SUCCESS);
2793 }
2794 
2795 /*@
2796   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2797 
2798   Not Collective
2799 
2800   Input Parameters:
2801 + dm   - The `DMPLEX`
2802 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2803 - size - The cone size for point `p`
2804 
2805   Level: beginner
2806 
2807   Note:
2808   This should be called after `DMPlexSetChart()`.
2809 
2810 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2811 @*/
2812 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2813 {
2814   DM_Plex *mesh = (DM_Plex *)dm->data;
2815 
2816   PetscFunctionBegin;
2817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2818   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2819   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2820   PetscFunctionReturn(PETSC_SUCCESS);
2821 }
2822 
2823 /*@C
2824   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2825 
2826   Not Collective
2827 
2828   Input Parameters:
2829 + dm - The `DMPLEX`
2830 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2831 
2832   Output Parameter:
2833 . cone - An array of points which are on the in-edges for point `p`
2834 
2835   Level: beginner
2836 
2837   Fortran Notes:
2838   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2839   `DMPlexRestoreCone()` is not needed/available in C.
2840 
2841 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2842 @*/
2843 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2844 {
2845   DM_Plex *mesh = (DM_Plex *)dm->data;
2846   PetscInt off;
2847 
2848   PetscFunctionBegin;
2849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2850   PetscAssertPointer(cone, 3);
2851   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2852   *cone = &mesh->cones[off];
2853   PetscFunctionReturn(PETSC_SUCCESS);
2854 }
2855 
2856 /*@C
2857   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2858 
2859   Not Collective
2860 
2861   Input Parameters:
2862 + dm - The `DMPLEX`
2863 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2864 
2865   Output Parameters:
2866 + pConesSection - `PetscSection` describing the layout of `pCones`
2867 - pCones        - An array of points which are on the in-edges for the point set `p`
2868 
2869   Level: intermediate
2870 
2871 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2872 @*/
2873 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2874 {
2875   PetscSection cs, newcs;
2876   PetscInt    *cones;
2877   PetscInt    *newarr = NULL;
2878   PetscInt     n;
2879 
2880   PetscFunctionBegin;
2881   PetscCall(DMPlexGetCones(dm, &cones));
2882   PetscCall(DMPlexGetConeSection(dm, &cs));
2883   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2884   if (pConesSection) *pConesSection = newcs;
2885   if (pCones) {
2886     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2887     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2888   }
2889   PetscFunctionReturn(PETSC_SUCCESS);
2890 }
2891 
2892 /*@
2893   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2894 
2895   Not Collective
2896 
2897   Input Parameters:
2898 + dm     - The `DMPLEX`
2899 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2900 
2901   Output Parameter:
2902 . expandedPoints - An array of vertices recursively expanded from input points
2903 
2904   Level: advanced
2905 
2906   Notes:
2907   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
2908 
2909   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2910 
2911 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2912           `DMPlexGetDepth()`, `IS`
2913 @*/
2914 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2915 {
2916   IS      *expandedPointsAll;
2917   PetscInt depth;
2918 
2919   PetscFunctionBegin;
2920   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2921   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2922   PetscAssertPointer(expandedPoints, 3);
2923   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2924   *expandedPoints = expandedPointsAll[0];
2925   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2926   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2927   PetscFunctionReturn(PETSC_SUCCESS);
2928 }
2929 
2930 /*@
2931   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).
2932 
2933   Not Collective
2934 
2935   Input Parameters:
2936 + dm     - The `DMPLEX`
2937 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2938 
2939   Output Parameters:
2940 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2941 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2942 - sections       - (optional) An array of sections which describe mappings from points to their cone points
2943 
2944   Level: advanced
2945 
2946   Notes:
2947   Like `DMPlexGetConeTuple()` but recursive.
2948 
2949   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.
2950   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2951 
2952   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:
2953   (1) DAG points in expandedPoints[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
2954   (2) DAG points in expandedPoints[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
2955 
2956 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2957           `DMPlexGetDepth()`, `PetscSection`, `IS`
2958 @*/
2959 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2960 {
2961   const PetscInt *arr0 = NULL, *cone = NULL;
2962   PetscInt       *arr = NULL, *newarr = NULL;
2963   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2964   IS             *expandedPoints_;
2965   PetscSection   *sections_;
2966 
2967   PetscFunctionBegin;
2968   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2969   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2970   if (depth) PetscAssertPointer(depth, 3);
2971   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
2972   if (sections) PetscAssertPointer(sections, 5);
2973   PetscCall(ISGetLocalSize(points, &n));
2974   PetscCall(ISGetIndices(points, &arr0));
2975   PetscCall(DMPlexGetDepth(dm, &depth_));
2976   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2977   PetscCall(PetscCalloc1(depth_, &sections_));
2978   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2979   for (d = depth_ - 1; d >= 0; d--) {
2980     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2981     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2982     for (i = 0; i < n; i++) {
2983       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2984       if (arr[i] >= start && arr[i] < end) {
2985         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2986         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2987       } else {
2988         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2989       }
2990     }
2991     PetscCall(PetscSectionSetUp(sections_[d]));
2992     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2993     PetscCall(PetscMalloc1(newn, &newarr));
2994     for (i = 0; i < n; i++) {
2995       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2996       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2997       if (cn > 1) {
2998         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2999         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3000       } else {
3001         newarr[co] = arr[i];
3002       }
3003     }
3004     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3005     arr = newarr;
3006     n   = newn;
3007   }
3008   PetscCall(ISRestoreIndices(points, &arr0));
3009   *depth = depth_;
3010   if (expandedPoints) *expandedPoints = expandedPoints_;
3011   else {
3012     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3013     PetscCall(PetscFree(expandedPoints_));
3014   }
3015   if (sections) *sections = sections_;
3016   else {
3017     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3018     PetscCall(PetscFree(sections_));
3019   }
3020   PetscFunctionReturn(PETSC_SUCCESS);
3021 }
3022 
3023 /*@
3024   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3025 
3026   Not Collective
3027 
3028   Input Parameters:
3029 + dm     - The `DMPLEX`
3030 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3031 
3032   Output Parameters:
3033 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3034 . expandedPoints - (optional) An array of recursively expanded cones
3035 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3036 
3037   Level: advanced
3038 
3039   Note:
3040   See `DMPlexGetConeRecursive()`
3041 
3042 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3043           `DMPlexGetDepth()`, `IS`, `PetscSection`
3044 @*/
3045 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3046 {
3047   PetscInt d, depth_;
3048 
3049   PetscFunctionBegin;
3050   PetscCall(DMPlexGetDepth(dm, &depth_));
3051   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3052   if (depth) *depth = 0;
3053   if (expandedPoints) {
3054     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3055     PetscCall(PetscFree(*expandedPoints));
3056   }
3057   if (sections) {
3058     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3059     PetscCall(PetscFree(*sections));
3060   }
3061   PetscFunctionReturn(PETSC_SUCCESS);
3062 }
3063 
3064 /*@
3065   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
3066 
3067   Not Collective
3068 
3069   Input Parameters:
3070 + dm   - The `DMPLEX`
3071 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3072 - cone - An array of points which are on the in-edges for point `p`
3073 
3074   Level: beginner
3075 
3076   Note:
3077   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3078 
3079 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3080 @*/
3081 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3082 {
3083   DM_Plex *mesh = (DM_Plex *)dm->data;
3084   PetscInt dof, off, c;
3085 
3086   PetscFunctionBegin;
3087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3088   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3089   if (dof) PetscAssertPointer(cone, 3);
3090   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3091   if (PetscDefined(USE_DEBUG)) {
3092     PetscInt pStart, pEnd;
3093     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3094     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3095     for (c = 0; c < dof; ++c) {
3096       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);
3097       mesh->cones[off + c] = cone[c];
3098     }
3099   } else {
3100     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3101   }
3102   PetscFunctionReturn(PETSC_SUCCESS);
3103 }
3104 
3105 /*@C
3106   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3107 
3108   Not Collective
3109 
3110   Input Parameters:
3111 + dm - The `DMPLEX`
3112 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3113 
3114   Output Parameter:
3115 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3116                     integer giving the prescription for cone traversal.
3117 
3118   Level: beginner
3119 
3120   Note:
3121   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3122   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3123   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3124   with the identity.
3125 
3126   Fortran Notes:
3127   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3128   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3129 
3130 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3131 @*/
3132 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3133 {
3134   DM_Plex *mesh = (DM_Plex *)dm->data;
3135   PetscInt off;
3136 
3137   PetscFunctionBegin;
3138   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3139   if (PetscDefined(USE_DEBUG)) {
3140     PetscInt dof;
3141     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3142     if (dof) PetscAssertPointer(coneOrientation, 3);
3143   }
3144   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3145 
3146   *coneOrientation = &mesh->coneOrientations[off];
3147   PetscFunctionReturn(PETSC_SUCCESS);
3148 }
3149 
3150 /*@
3151   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3152 
3153   Not Collective
3154 
3155   Input Parameters:
3156 + dm              - The `DMPLEX`
3157 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3158 - coneOrientation - An array of orientations
3159 
3160   Level: beginner
3161 
3162   Notes:
3163   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3164 
3165   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3166 
3167 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3168 @*/
3169 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3170 {
3171   DM_Plex *mesh = (DM_Plex *)dm->data;
3172   PetscInt pStart, pEnd;
3173   PetscInt dof, off, c;
3174 
3175   PetscFunctionBegin;
3176   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3177   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3178   if (dof) PetscAssertPointer(coneOrientation, 3);
3179   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3180   if (PetscDefined(USE_DEBUG)) {
3181     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3182     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);
3183     for (c = 0; c < dof; ++c) {
3184       PetscInt cdof, o = coneOrientation[c];
3185 
3186       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3187       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);
3188       mesh->coneOrientations[off + c] = o;
3189     }
3190   } else {
3191     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3192   }
3193   PetscFunctionReturn(PETSC_SUCCESS);
3194 }
3195 
3196 /*@
3197   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3198 
3199   Not Collective
3200 
3201   Input Parameters:
3202 + dm        - The `DMPLEX`
3203 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3204 . conePos   - The local index in the cone where the point should be put
3205 - conePoint - The mesh point to insert
3206 
3207   Level: beginner
3208 
3209 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3210 @*/
3211 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3212 {
3213   DM_Plex *mesh = (DM_Plex *)dm->data;
3214   PetscInt pStart, pEnd;
3215   PetscInt dof, off;
3216 
3217   PetscFunctionBegin;
3218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3219   if (PetscDefined(USE_DEBUG)) {
3220     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3221     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);
3222     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);
3223     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3224     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);
3225   }
3226   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3227   mesh->cones[off + conePos] = conePoint;
3228   PetscFunctionReturn(PETSC_SUCCESS);
3229 }
3230 
3231 /*@
3232   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3233 
3234   Not Collective
3235 
3236   Input Parameters:
3237 + dm              - The `DMPLEX`
3238 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3239 . conePos         - The local index in the cone where the point should be put
3240 - coneOrientation - The point orientation to insert
3241 
3242   Level: beginner
3243 
3244   Note:
3245   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3246 
3247 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3248 @*/
3249 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3250 {
3251   DM_Plex *mesh = (DM_Plex *)dm->data;
3252   PetscInt pStart, pEnd;
3253   PetscInt dof, off;
3254 
3255   PetscFunctionBegin;
3256   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3257   if (PetscDefined(USE_DEBUG)) {
3258     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3259     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);
3260     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3261     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);
3262   }
3263   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3264   mesh->coneOrientations[off + conePos] = coneOrientation;
3265   PetscFunctionReturn(PETSC_SUCCESS);
3266 }
3267 
3268 /*@C
3269   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3270 
3271   Not collective
3272 
3273   Input Parameters:
3274 + dm - The DMPlex
3275 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3276 
3277   Output Parameters:
3278 + cone - An array of points which are on the in-edges for point `p`
3279 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3280         integer giving the prescription for cone traversal.
3281 
3282   Level: beginner
3283 
3284   Notes:
3285   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3286   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3287   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3288   with the identity.
3289 
3290   Fortran Notes:
3291   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3292   `DMPlexRestoreCone()` is not needed/available in C.
3293 
3294 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3295 @*/
3296 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3297 {
3298   DM_Plex *mesh = (DM_Plex *)dm->data;
3299 
3300   PetscFunctionBegin;
3301   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3302   if (mesh->tr) {
3303     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3304   } else {
3305     PetscInt off;
3306     if (PetscDefined(USE_DEBUG)) {
3307       PetscInt dof;
3308       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3309       if (dof) {
3310         if (cone) PetscAssertPointer(cone, 3);
3311         if (ornt) PetscAssertPointer(ornt, 4);
3312       }
3313     }
3314     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3315     if (cone) *cone = mesh->cones ? mesh->cones + off : NULL; // NULL + 0 is UB
3316     if (ornt) *ornt = mesh->coneOrientations ? mesh->coneOrientations + off : NULL;
3317   }
3318   PetscFunctionReturn(PETSC_SUCCESS);
3319 }
3320 
3321 /*@C
3322   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3323 
3324   Not Collective
3325 
3326   Input Parameters:
3327 + dm   - The DMPlex
3328 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3329 . cone - An array of points which are on the in-edges for point p
3330 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3331         integer giving the prescription for cone traversal.
3332 
3333   Level: beginner
3334 
3335   Notes:
3336   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3337   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3338   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3339   with the identity.
3340 
3341   Fortran Notes:
3342   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3343   `DMPlexRestoreCone()` is not needed/available in C.
3344 
3345 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3346 @*/
3347 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3348 {
3349   DM_Plex *mesh = (DM_Plex *)dm->data;
3350 
3351   PetscFunctionBegin;
3352   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3353   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3354   PetscFunctionReturn(PETSC_SUCCESS);
3355 }
3356 
3357 /*@
3358   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3359 
3360   Not Collective
3361 
3362   Input Parameters:
3363 + dm - The `DMPLEX`
3364 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3365 
3366   Output Parameter:
3367 . size - The support size for point `p`
3368 
3369   Level: beginner
3370 
3371 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3372 @*/
3373 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3374 {
3375   DM_Plex *mesh = (DM_Plex *)dm->data;
3376 
3377   PetscFunctionBegin;
3378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3379   PetscAssertPointer(size, 3);
3380   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3381   PetscFunctionReturn(PETSC_SUCCESS);
3382 }
3383 
3384 /*@
3385   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3386 
3387   Not Collective
3388 
3389   Input Parameters:
3390 + dm   - The `DMPLEX`
3391 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3392 - size - The support size for point `p`
3393 
3394   Level: beginner
3395 
3396   Note:
3397   This should be called after `DMPlexSetChart()`.
3398 
3399 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3400 @*/
3401 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3402 {
3403   DM_Plex *mesh = (DM_Plex *)dm->data;
3404 
3405   PetscFunctionBegin;
3406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3407   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3408   PetscFunctionReturn(PETSC_SUCCESS);
3409 }
3410 
3411 /*@C
3412   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3413 
3414   Not Collective
3415 
3416   Input Parameters:
3417 + dm - The `DMPLEX`
3418 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3419 
3420   Output Parameter:
3421 . support - An array of points which are on the out-edges for point `p`
3422 
3423   Level: beginner
3424 
3425   Fortran Notes:
3426   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3427   `DMPlexRestoreSupport()` is not needed/available in C.
3428 
3429 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3430 @*/
3431 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3432 {
3433   DM_Plex *mesh = (DM_Plex *)dm->data;
3434   PetscInt off;
3435 
3436   PetscFunctionBegin;
3437   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3438   PetscAssertPointer(support, 3);
3439   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3440   *support = mesh->supports ? mesh->supports + off : NULL; //NULL + 0 is UB
3441   PetscFunctionReturn(PETSC_SUCCESS);
3442 }
3443 
3444 /*@
3445   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3446 
3447   Not Collective
3448 
3449   Input Parameters:
3450 + dm      - The `DMPLEX`
3451 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3452 - support - An array of points which are on the out-edges for point `p`
3453 
3454   Level: beginner
3455 
3456   Note:
3457   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3458 
3459 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3460 @*/
3461 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3462 {
3463   DM_Plex *mesh = (DM_Plex *)dm->data;
3464   PetscInt pStart, pEnd;
3465   PetscInt dof, off, c;
3466 
3467   PetscFunctionBegin;
3468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3469   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3470   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3471   if (dof) PetscAssertPointer(support, 3);
3472   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3473   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);
3474   for (c = 0; c < dof; ++c) {
3475     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);
3476     mesh->supports[off + c] = support[c];
3477   }
3478   PetscFunctionReturn(PETSC_SUCCESS);
3479 }
3480 
3481 /*@
3482   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3483 
3484   Not Collective
3485 
3486   Input Parameters:
3487 + dm           - The `DMPLEX`
3488 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3489 . supportPos   - The local index in the cone where the point should be put
3490 - supportPoint - The mesh point to insert
3491 
3492   Level: beginner
3493 
3494 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3495 @*/
3496 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3497 {
3498   DM_Plex *mesh = (DM_Plex *)dm->data;
3499   PetscInt pStart, pEnd;
3500   PetscInt dof, off;
3501 
3502   PetscFunctionBegin;
3503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3504   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3505   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3506   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3507   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);
3508   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);
3509   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);
3510   mesh->supports[off + supportPos] = supportPoint;
3511   PetscFunctionReturn(PETSC_SUCCESS);
3512 }
3513 
3514 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3515 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3516 {
3517   switch (ct) {
3518   case DM_POLYTOPE_SEGMENT:
3519     if (o == -1) return -2;
3520     break;
3521   case DM_POLYTOPE_TRIANGLE:
3522     if (o == -3) return -1;
3523     if (o == -2) return -3;
3524     if (o == -1) return -2;
3525     break;
3526   case DM_POLYTOPE_QUADRILATERAL:
3527     if (o == -4) return -2;
3528     if (o == -3) return -1;
3529     if (o == -2) return -4;
3530     if (o == -1) return -3;
3531     break;
3532   default:
3533     return o;
3534   }
3535   return o;
3536 }
3537 
3538 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3539 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3540 {
3541   switch (ct) {
3542   case DM_POLYTOPE_SEGMENT:
3543     if ((o == -2) || (o == 1)) return -1;
3544     if (o == -1) return 0;
3545     break;
3546   case DM_POLYTOPE_TRIANGLE:
3547     if (o == -3) return -2;
3548     if (o == -2) return -1;
3549     if (o == -1) return -3;
3550     break;
3551   case DM_POLYTOPE_QUADRILATERAL:
3552     if (o == -4) return -2;
3553     if (o == -3) return -1;
3554     if (o == -2) return -4;
3555     if (o == -1) return -3;
3556     break;
3557   default:
3558     return o;
3559   }
3560   return o;
3561 }
3562 
3563 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3564 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3565 {
3566   PetscInt pStart, pEnd, p;
3567 
3568   PetscFunctionBegin;
3569   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3570   for (p = pStart; p < pEnd; ++p) {
3571     const PetscInt *cone, *ornt;
3572     PetscInt        coneSize, c;
3573 
3574     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3575     PetscCall(DMPlexGetCone(dm, p, &cone));
3576     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3577     for (c = 0; c < coneSize; ++c) {
3578       DMPolytopeType ct;
3579       const PetscInt o = ornt[c];
3580 
3581       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3582       switch (ct) {
3583       case DM_POLYTOPE_SEGMENT:
3584         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3585         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3586         break;
3587       case DM_POLYTOPE_TRIANGLE:
3588         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3589         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3590         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3591         break;
3592       case DM_POLYTOPE_QUADRILATERAL:
3593         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3594         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3595         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3596         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3597         break;
3598       default:
3599         break;
3600       }
3601     }
3602   }
3603   PetscFunctionReturn(PETSC_SUCCESS);
3604 }
3605 
3606 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3607 {
3608   DM_Plex *mesh = (DM_Plex *)dm->data;
3609 
3610   PetscFunctionBeginHot;
3611   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3612     if (useCone) {
3613       PetscCall(DMPlexGetConeSize(dm, p, size));
3614       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3615     } else {
3616       PetscCall(DMPlexGetSupportSize(dm, p, size));
3617       PetscCall(DMPlexGetSupport(dm, p, arr));
3618     }
3619   } else {
3620     if (useCone) {
3621       const PetscSection s   = mesh->coneSection;
3622       const PetscInt     ps  = p - s->pStart;
3623       const PetscInt     off = s->atlasOff[ps];
3624 
3625       *size = s->atlasDof[ps];
3626       *arr  = mesh->cones + off;
3627       *ornt = mesh->coneOrientations + off;
3628     } else {
3629       const PetscSection s   = mesh->supportSection;
3630       const PetscInt     ps  = p - s->pStart;
3631       const PetscInt     off = s->atlasOff[ps];
3632 
3633       *size = s->atlasDof[ps];
3634       *arr  = mesh->supports + off;
3635     }
3636   }
3637   PetscFunctionReturn(PETSC_SUCCESS);
3638 }
3639 
3640 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3641 {
3642   DM_Plex *mesh = (DM_Plex *)dm->data;
3643 
3644   PetscFunctionBeginHot;
3645   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3646     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3647   }
3648   PetscFunctionReturn(PETSC_SUCCESS);
3649 }
3650 
3651 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3652 {
3653   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3654   PetscInt       *closure;
3655   const PetscInt *tmp = NULL, *tmpO = NULL;
3656   PetscInt        off = 0, tmpSize, t;
3657 
3658   PetscFunctionBeginHot;
3659   if (ornt) {
3660     PetscCall(DMPlexGetCellType(dm, p, &ct));
3661     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3662   }
3663   if (*points) {
3664     closure = *points;
3665   } else {
3666     PetscInt maxConeSize, maxSupportSize;
3667     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3668     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3669   }
3670   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3671   if (ct == DM_POLYTOPE_UNKNOWN) {
3672     closure[off++] = p;
3673     closure[off++] = 0;
3674     for (t = 0; t < tmpSize; ++t) {
3675       closure[off++] = tmp[t];
3676       closure[off++] = tmpO ? tmpO[t] : 0;
3677     }
3678   } else {
3679     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3680 
3681     /* We assume that cells with a valid type have faces with a valid type */
3682     closure[off++] = p;
3683     closure[off++] = ornt;
3684     for (t = 0; t < tmpSize; ++t) {
3685       DMPolytopeType ft;
3686 
3687       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3688       closure[off++] = tmp[arr[t]];
3689       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3690     }
3691   }
3692   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3693   if (numPoints) *numPoints = tmpSize + 1;
3694   if (points) *points = closure;
3695   PetscFunctionReturn(PETSC_SUCCESS);
3696 }
3697 
3698 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3699 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3700 {
3701   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3702   const PetscInt *cone, *ornt;
3703   PetscInt       *pts, *closure = NULL;
3704   DMPolytopeType  ft;
3705   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3706   PetscInt        dim, coneSize, c, d, clSize, cl;
3707 
3708   PetscFunctionBeginHot;
3709   PetscCall(DMGetDimension(dm, &dim));
3710   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3711   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3712   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3713   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3714   maxSize       = PetscMax(coneSeries, supportSeries);
3715   if (*points) {
3716     pts = *points;
3717   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3718   c        = 0;
3719   pts[c++] = point;
3720   pts[c++] = o;
3721   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3722   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3723   for (cl = 0; cl < clSize * 2; cl += 2) {
3724     pts[c++] = closure[cl];
3725     pts[c++] = closure[cl + 1];
3726   }
3727   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3728   for (cl = 0; cl < clSize * 2; cl += 2) {
3729     pts[c++] = closure[cl];
3730     pts[c++] = closure[cl + 1];
3731   }
3732   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3733   for (d = 2; d < coneSize; ++d) {
3734     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3735     pts[c++] = cone[arr[d * 2 + 0]];
3736     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3737   }
3738   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3739   if (dim >= 3) {
3740     for (d = 2; d < coneSize; ++d) {
3741       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3742       const PetscInt *fcone, *fornt;
3743       PetscInt        fconeSize, fc, i;
3744 
3745       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3746       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3747       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3748       for (fc = 0; fc < fconeSize; ++fc) {
3749         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3750         const PetscInt co = farr[fc * 2 + 1];
3751 
3752         for (i = 0; i < c; i += 2)
3753           if (pts[i] == cp) break;
3754         if (i == c) {
3755           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3756           pts[c++] = cp;
3757           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3758         }
3759       }
3760       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3761     }
3762   }
3763   *numPoints = c / 2;
3764   *points    = pts;
3765   PetscFunctionReturn(PETSC_SUCCESS);
3766 }
3767 
3768 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3769 {
3770   DMPolytopeType ct;
3771   PetscInt      *closure, *fifo;
3772   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3773   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3774   PetscInt       depth, maxSize;
3775 
3776   PetscFunctionBeginHot;
3777   PetscCall(DMPlexGetDepth(dm, &depth));
3778   if (depth == 1) {
3779     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3780     PetscFunctionReturn(PETSC_SUCCESS);
3781   }
3782   PetscCall(DMPlexGetCellType(dm, p, &ct));
3783   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3784   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3785     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3786     PetscFunctionReturn(PETSC_SUCCESS);
3787   }
3788   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3789   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3790   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3791   maxSize       = PetscMax(coneSeries, supportSeries);
3792   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3793   if (*points) {
3794     closure = *points;
3795   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3796   closure[closureSize++] = p;
3797   closure[closureSize++] = ornt;
3798   fifo[fifoSize++]       = p;
3799   fifo[fifoSize++]       = ornt;
3800   fifo[fifoSize++]       = ct;
3801   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3802   while (fifoSize - fifoStart) {
3803     const PetscInt       q    = fifo[fifoStart++];
3804     const PetscInt       o    = fifo[fifoStart++];
3805     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3806     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3807     const PetscInt      *tmp, *tmpO = NULL;
3808     PetscInt             tmpSize, t;
3809 
3810     if (PetscDefined(USE_DEBUG)) {
3811       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3812       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);
3813     }
3814     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3815     for (t = 0; t < tmpSize; ++t) {
3816       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3817       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3818       const PetscInt cp = tmp[ip];
3819       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3820       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3821       PetscInt       c;
3822 
3823       /* Check for duplicate */
3824       for (c = 0; c < closureSize; c += 2) {
3825         if (closure[c] == cp) break;
3826       }
3827       if (c == closureSize) {
3828         closure[closureSize++] = cp;
3829         closure[closureSize++] = co;
3830         fifo[fifoSize++]       = cp;
3831         fifo[fifoSize++]       = co;
3832         fifo[fifoSize++]       = ct;
3833       }
3834     }
3835     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3836   }
3837   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3838   if (numPoints) *numPoints = closureSize / 2;
3839   if (points) *points = closure;
3840   PetscFunctionReturn(PETSC_SUCCESS);
3841 }
3842 
3843 /*@C
3844   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3845 
3846   Not Collective
3847 
3848   Input Parameters:
3849 + dm      - The `DMPLEX`
3850 . p       - The mesh point
3851 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3852 
3853   Input/Output Parameter:
3854 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3855            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
3856 
3857   Output Parameter:
3858 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3859 
3860   Level: beginner
3861 
3862   Note:
3863   If using internal storage (points is `NULL` on input), each call overwrites the last output.
3864 
3865   Fortran Notes:
3866   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
3867 
3868 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3869 @*/
3870 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3871 {
3872   PetscFunctionBeginHot;
3873   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3874   if (numPoints) PetscAssertPointer(numPoints, 4);
3875   if (points) PetscAssertPointer(points, 5);
3876   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3877   PetscFunctionReturn(PETSC_SUCCESS);
3878 }
3879 
3880 /*@C
3881   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3882 
3883   Not Collective
3884 
3885   Input Parameters:
3886 + dm        - The `DMPLEX`
3887 . p         - The mesh point
3888 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3889 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3890 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3891 
3892   Level: beginner
3893 
3894   Note:
3895   If not using internal storage (points is not `NULL` on input), this call is unnecessary
3896 
3897 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3898 @*/
3899 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3900 {
3901   PetscFunctionBeginHot;
3902   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3903   if (numPoints) *numPoints = 0;
3904   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3905   PetscFunctionReturn(PETSC_SUCCESS);
3906 }
3907 
3908 /*@
3909   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3910 
3911   Not Collective
3912 
3913   Input Parameter:
3914 . dm - The `DMPLEX`
3915 
3916   Output Parameters:
3917 + maxConeSize    - The maximum number of in-edges
3918 - maxSupportSize - The maximum number of out-edges
3919 
3920   Level: beginner
3921 
3922 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3923 @*/
3924 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3925 {
3926   DM_Plex *mesh = (DM_Plex *)dm->data;
3927 
3928   PetscFunctionBegin;
3929   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3930   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3931   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3932   PetscFunctionReturn(PETSC_SUCCESS);
3933 }
3934 
3935 PetscErrorCode DMSetUp_Plex(DM dm)
3936 {
3937   DM_Plex *mesh = (DM_Plex *)dm->data;
3938   PetscInt size, maxSupportSize;
3939 
3940   PetscFunctionBegin;
3941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3942   PetscCall(PetscSectionSetUp(mesh->coneSection));
3943   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3944   PetscCall(PetscMalloc1(size, &mesh->cones));
3945   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3946   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3947   if (maxSupportSize) {
3948     PetscCall(PetscSectionSetUp(mesh->supportSection));
3949     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3950     PetscCall(PetscMalloc1(size, &mesh->supports));
3951   }
3952   PetscFunctionReturn(PETSC_SUCCESS);
3953 }
3954 
3955 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3956 {
3957   PetscFunctionBegin;
3958   if (subdm) PetscCall(DMClone(dm, subdm));
3959   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3960   if (subdm) (*subdm)->useNatural = dm->useNatural;
3961   if (dm->useNatural && dm->sfMigration) {
3962     PetscSF sfNatural;
3963 
3964     (*subdm)->sfMigration = dm->sfMigration;
3965     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3966     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3967     (*subdm)->sfNatural = sfNatural;
3968   }
3969   PetscFunctionReturn(PETSC_SUCCESS);
3970 }
3971 
3972 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3973 {
3974   PetscInt i = 0;
3975 
3976   PetscFunctionBegin;
3977   PetscCall(DMClone(dms[0], superdm));
3978   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3979   (*superdm)->useNatural = PETSC_FALSE;
3980   for (i = 0; i < len; i++) {
3981     if (dms[i]->useNatural && dms[i]->sfMigration) {
3982       PetscSF sfNatural;
3983 
3984       (*superdm)->sfMigration = dms[i]->sfMigration;
3985       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3986       (*superdm)->useNatural = PETSC_TRUE;
3987       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3988       (*superdm)->sfNatural = sfNatural;
3989       break;
3990     }
3991   }
3992   PetscFunctionReturn(PETSC_SUCCESS);
3993 }
3994 
3995 /*@
3996   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3997 
3998   Not Collective
3999 
4000   Input Parameter:
4001 . dm - The `DMPLEX`
4002 
4003   Level: beginner
4004 
4005   Note:
4006   This should be called after all calls to `DMPlexSetCone()`
4007 
4008 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4009 @*/
4010 PetscErrorCode DMPlexSymmetrize(DM dm)
4011 {
4012   DM_Plex  *mesh = (DM_Plex *)dm->data;
4013   PetscInt *offsets;
4014   PetscInt  supportSize;
4015   PetscInt  pStart, pEnd, p;
4016 
4017   PetscFunctionBegin;
4018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4019   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4020   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4021   /* Calculate support sizes */
4022   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4023   for (p = pStart; p < pEnd; ++p) {
4024     PetscInt dof, off, c;
4025 
4026     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4027     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4028     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4029   }
4030   PetscCall(PetscSectionSetUp(mesh->supportSection));
4031   /* Calculate supports */
4032   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4033   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4034   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4035   for (p = pStart; p < pEnd; ++p) {
4036     PetscInt dof, off, c;
4037 
4038     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4039     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4040     for (c = off; c < off + dof; ++c) {
4041       const PetscInt q = mesh->cones[c];
4042       PetscInt       offS;
4043 
4044       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4045 
4046       mesh->supports[offS + offsets[q]] = p;
4047       ++offsets[q];
4048     }
4049   }
4050   PetscCall(PetscFree(offsets));
4051   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4052   PetscFunctionReturn(PETSC_SUCCESS);
4053 }
4054 
4055 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4056 {
4057   IS stratumIS;
4058 
4059   PetscFunctionBegin;
4060   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4061   if (PetscDefined(USE_DEBUG)) {
4062     PetscInt  qStart, qEnd, numLevels, level;
4063     PetscBool overlap = PETSC_FALSE;
4064     PetscCall(DMLabelGetNumValues(label, &numLevels));
4065     for (level = 0; level < numLevels; level++) {
4066       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4067       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4068         overlap = PETSC_TRUE;
4069         break;
4070       }
4071     }
4072     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);
4073   }
4074   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4075   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4076   PetscCall(ISDestroy(&stratumIS));
4077   PetscFunctionReturn(PETSC_SUCCESS);
4078 }
4079 
4080 /*@
4081   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4082   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4083   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4084   the DAG.
4085 
4086   Collective
4087 
4088   Input Parameter:
4089 . dm - The `DMPLEX`
4090 
4091   Level: beginner
4092 
4093   Notes:
4094   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4095   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4096   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4097   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4098   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4099 
4100   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4101   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4102   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
4103   to interpolate only that one (e0), so that
4104 .vb
4105   cone(c0) = {e0, v2}
4106   cone(e0) = {v0, v1}
4107 .ve
4108   If `DMPlexStratify()` is run on this mesh, it will give depths
4109 .vb
4110    depth 0 = {v0, v1, v2}
4111    depth 1 = {e0, c0}
4112 .ve
4113   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4114 
4115   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4116 
4117 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4118 @*/
4119 PetscErrorCode DMPlexStratify(DM dm)
4120 {
4121   DM_Plex *mesh = (DM_Plex *)dm->data;
4122   DMLabel  label;
4123   PetscInt pStart, pEnd, p;
4124   PetscInt numRoots = 0, numLeaves = 0;
4125 
4126   PetscFunctionBegin;
4127   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4128   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4129 
4130   /* Create depth label */
4131   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4132   PetscCall(DMCreateLabel(dm, "depth"));
4133   PetscCall(DMPlexGetDepthLabel(dm, &label));
4134 
4135   {
4136     /* Initialize roots and count leaves */
4137     PetscInt sMin = PETSC_MAX_INT;
4138     PetscInt sMax = PETSC_MIN_INT;
4139     PetscInt coneSize, supportSize;
4140 
4141     for (p = pStart; p < pEnd; ++p) {
4142       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4143       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4144       if (!coneSize && supportSize) {
4145         sMin = PetscMin(p, sMin);
4146         sMax = PetscMax(p, sMax);
4147         ++numRoots;
4148       } else if (!supportSize && coneSize) {
4149         ++numLeaves;
4150       } else if (!supportSize && !coneSize) {
4151         /* Isolated points */
4152         sMin = PetscMin(p, sMin);
4153         sMax = PetscMax(p, sMax);
4154       }
4155     }
4156     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4157   }
4158 
4159   if (numRoots + numLeaves == (pEnd - pStart)) {
4160     PetscInt sMin = PETSC_MAX_INT;
4161     PetscInt sMax = PETSC_MIN_INT;
4162     PetscInt coneSize, supportSize;
4163 
4164     for (p = pStart; p < pEnd; ++p) {
4165       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4166       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4167       if (!supportSize && coneSize) {
4168         sMin = PetscMin(p, sMin);
4169         sMax = PetscMax(p, sMax);
4170       }
4171     }
4172     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4173   } else {
4174     PetscInt level = 0;
4175     PetscInt qStart, qEnd, q;
4176 
4177     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4178     while (qEnd > qStart) {
4179       PetscInt sMin = PETSC_MAX_INT;
4180       PetscInt sMax = PETSC_MIN_INT;
4181 
4182       for (q = qStart; q < qEnd; ++q) {
4183         const PetscInt *support;
4184         PetscInt        supportSize, s;
4185 
4186         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4187         PetscCall(DMPlexGetSupport(dm, q, &support));
4188         for (s = 0; s < supportSize; ++s) {
4189           sMin = PetscMin(support[s], sMin);
4190           sMax = PetscMax(support[s], sMax);
4191         }
4192       }
4193       PetscCall(DMLabelGetNumValues(label, &level));
4194       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4195       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4196     }
4197   }
4198   { /* just in case there is an empty process */
4199     PetscInt numValues, maxValues = 0, v;
4200 
4201     PetscCall(DMLabelGetNumValues(label, &numValues));
4202     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4203     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4204   }
4205   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4206   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4207   PetscFunctionReturn(PETSC_SUCCESS);
4208 }
4209 
4210 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4211 {
4212   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4213   PetscInt       dim, depth, pheight, coneSize;
4214 
4215   PetscFunctionBeginHot;
4216   PetscCall(DMGetDimension(dm, &dim));
4217   PetscCall(DMPlexGetDepth(dm, &depth));
4218   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4219   pheight = depth - pdepth;
4220   if (depth <= 1) {
4221     switch (pdepth) {
4222     case 0:
4223       ct = DM_POLYTOPE_POINT;
4224       break;
4225     case 1:
4226       switch (coneSize) {
4227       case 2:
4228         ct = DM_POLYTOPE_SEGMENT;
4229         break;
4230       case 3:
4231         ct = DM_POLYTOPE_TRIANGLE;
4232         break;
4233       case 4:
4234         switch (dim) {
4235         case 2:
4236           ct = DM_POLYTOPE_QUADRILATERAL;
4237           break;
4238         case 3:
4239           ct = DM_POLYTOPE_TETRAHEDRON;
4240           break;
4241         default:
4242           break;
4243         }
4244         break;
4245       case 5:
4246         ct = DM_POLYTOPE_PYRAMID;
4247         break;
4248       case 6:
4249         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4250         break;
4251       case 8:
4252         ct = DM_POLYTOPE_HEXAHEDRON;
4253         break;
4254       default:
4255         break;
4256       }
4257     }
4258   } else {
4259     if (pdepth == 0) {
4260       ct = DM_POLYTOPE_POINT;
4261     } else if (pheight == 0) {
4262       switch (dim) {
4263       case 1:
4264         switch (coneSize) {
4265         case 2:
4266           ct = DM_POLYTOPE_SEGMENT;
4267           break;
4268         default:
4269           break;
4270         }
4271         break;
4272       case 2:
4273         switch (coneSize) {
4274         case 3:
4275           ct = DM_POLYTOPE_TRIANGLE;
4276           break;
4277         case 4:
4278           ct = DM_POLYTOPE_QUADRILATERAL;
4279           break;
4280         default:
4281           break;
4282         }
4283         break;
4284       case 3:
4285         switch (coneSize) {
4286         case 4:
4287           ct = DM_POLYTOPE_TETRAHEDRON;
4288           break;
4289         case 5: {
4290           const PetscInt *cone;
4291           PetscInt        faceConeSize;
4292 
4293           PetscCall(DMPlexGetCone(dm, p, &cone));
4294           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4295           switch (faceConeSize) {
4296           case 3:
4297             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4298             break;
4299           case 4:
4300             ct = DM_POLYTOPE_PYRAMID;
4301             break;
4302           }
4303         } break;
4304         case 6:
4305           ct = DM_POLYTOPE_HEXAHEDRON;
4306           break;
4307         default:
4308           break;
4309         }
4310         break;
4311       default:
4312         break;
4313       }
4314     } else if (pheight > 0) {
4315       switch (coneSize) {
4316       case 2:
4317         ct = DM_POLYTOPE_SEGMENT;
4318         break;
4319       case 3:
4320         ct = DM_POLYTOPE_TRIANGLE;
4321         break;
4322       case 4:
4323         ct = DM_POLYTOPE_QUADRILATERAL;
4324         break;
4325       default:
4326         break;
4327       }
4328     }
4329   }
4330   *pt = ct;
4331   PetscFunctionReturn(PETSC_SUCCESS);
4332 }
4333 
4334 /*@
4335   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4336 
4337   Collective
4338 
4339   Input Parameter:
4340 . dm - The `DMPLEX`
4341 
4342   Level: developer
4343 
4344   Note:
4345   This function is normally called automatically when a cell type is requested. It creates an
4346   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4347   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4348 
4349   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4350 
4351 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4352 @*/
4353 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4354 {
4355   DM_Plex *mesh;
4356   DMLabel  ctLabel;
4357   PetscInt pStart, pEnd, p;
4358 
4359   PetscFunctionBegin;
4360   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4361   mesh = (DM_Plex *)dm->data;
4362   PetscCall(DMCreateLabel(dm, "celltype"));
4363   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4364   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4365   PetscCall(PetscFree(mesh->cellTypes));
4366   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4367   for (p = pStart; p < pEnd; ++p) {
4368     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4369     PetscInt       pdepth;
4370 
4371     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4372     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4373     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4374     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4375     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4376   }
4377   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4378   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4379   PetscFunctionReturn(PETSC_SUCCESS);
4380 }
4381 
4382 /*@C
4383   DMPlexGetJoin - Get an array for the join of the set of points
4384 
4385   Not Collective
4386 
4387   Input Parameters:
4388 + dm        - The `DMPLEX` object
4389 . numPoints - The number of input points for the join
4390 - points    - The input points
4391 
4392   Output Parameters:
4393 + numCoveredPoints - The number of points in the join
4394 - coveredPoints    - The points in the join
4395 
4396   Level: intermediate
4397 
4398   Note:
4399   Currently, this is restricted to a single level join
4400 
4401   Fortran Notes:
4402   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4403 
4404 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4405 @*/
4406 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4407 {
4408   DM_Plex  *mesh = (DM_Plex *)dm->data;
4409   PetscInt *join[2];
4410   PetscInt  joinSize, i = 0;
4411   PetscInt  dof, off, p, c, m;
4412   PetscInt  maxSupportSize;
4413 
4414   PetscFunctionBegin;
4415   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4416   PetscAssertPointer(points, 3);
4417   PetscAssertPointer(numCoveredPoints, 4);
4418   PetscAssertPointer(coveredPoints, 5);
4419   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4420   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4421   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4422   /* Copy in support of first point */
4423   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4424   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4425   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4426   /* Check each successive support */
4427   for (p = 1; p < numPoints; ++p) {
4428     PetscInt newJoinSize = 0;
4429 
4430     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4431     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4432     for (c = 0; c < dof; ++c) {
4433       const PetscInt point = mesh->supports[off + c];
4434 
4435       for (m = 0; m < joinSize; ++m) {
4436         if (point == join[i][m]) {
4437           join[1 - i][newJoinSize++] = point;
4438           break;
4439         }
4440       }
4441     }
4442     joinSize = newJoinSize;
4443     i        = 1 - i;
4444   }
4445   *numCoveredPoints = joinSize;
4446   *coveredPoints    = join[i];
4447   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4448   PetscFunctionReturn(PETSC_SUCCESS);
4449 }
4450 
4451 /*@C
4452   DMPlexRestoreJoin - Restore an array for the join of the set of points
4453 
4454   Not Collective
4455 
4456   Input Parameters:
4457 + dm        - The `DMPLEX` object
4458 . numPoints - The number of input points for the join
4459 - points    - The input points
4460 
4461   Output Parameters:
4462 + numCoveredPoints - The number of points in the join
4463 - coveredPoints    - The points in the join
4464 
4465   Level: intermediate
4466 
4467   Fortran Notes:
4468   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4469 
4470 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4471 @*/
4472 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4473 {
4474   PetscFunctionBegin;
4475   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4476   if (points) PetscAssertPointer(points, 3);
4477   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4478   PetscAssertPointer(coveredPoints, 5);
4479   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4480   if (numCoveredPoints) *numCoveredPoints = 0;
4481   PetscFunctionReturn(PETSC_SUCCESS);
4482 }
4483 
4484 /*@C
4485   DMPlexGetFullJoin - Get an array for the join of the set of points
4486 
4487   Not Collective
4488 
4489   Input Parameters:
4490 + dm        - The `DMPLEX` object
4491 . numPoints - The number of input points for the join
4492 - points    - The input points
4493 
4494   Output Parameters:
4495 + numCoveredPoints - The number of points in the join
4496 - coveredPoints    - The points in the join
4497 
4498   Level: intermediate
4499 
4500   Fortran Notes:
4501   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4502 
4503 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4504 @*/
4505 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4506 {
4507   PetscInt *offsets, **closures;
4508   PetscInt *join[2];
4509   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4510   PetscInt  p, d, c, m, ms;
4511 
4512   PetscFunctionBegin;
4513   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4514   PetscAssertPointer(points, 3);
4515   PetscAssertPointer(numCoveredPoints, 4);
4516   PetscAssertPointer(coveredPoints, 5);
4517 
4518   PetscCall(DMPlexGetDepth(dm, &depth));
4519   PetscCall(PetscCalloc1(numPoints, &closures));
4520   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4521   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4522   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4523   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4524   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4525 
4526   for (p = 0; p < numPoints; ++p) {
4527     PetscInt closureSize;
4528 
4529     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4530 
4531     offsets[p * (depth + 2) + 0] = 0;
4532     for (d = 0; d < depth + 1; ++d) {
4533       PetscInt pStart, pEnd, i;
4534 
4535       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4536       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4537         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4538           offsets[p * (depth + 2) + d + 1] = i;
4539           break;
4540         }
4541       }
4542       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4543     }
4544     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);
4545   }
4546   for (d = 0; d < depth + 1; ++d) {
4547     PetscInt dof;
4548 
4549     /* Copy in support of first point */
4550     dof = offsets[d + 1] - offsets[d];
4551     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4552     /* Check each successive cone */
4553     for (p = 1; p < numPoints && joinSize; ++p) {
4554       PetscInt newJoinSize = 0;
4555 
4556       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4557       for (c = 0; c < dof; ++c) {
4558         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4559 
4560         for (m = 0; m < joinSize; ++m) {
4561           if (point == join[i][m]) {
4562             join[1 - i][newJoinSize++] = point;
4563             break;
4564           }
4565         }
4566       }
4567       joinSize = newJoinSize;
4568       i        = 1 - i;
4569     }
4570     if (joinSize) break;
4571   }
4572   *numCoveredPoints = joinSize;
4573   *coveredPoints    = join[i];
4574   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4575   PetscCall(PetscFree(closures));
4576   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4577   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4578   PetscFunctionReturn(PETSC_SUCCESS);
4579 }
4580 
4581 /*@C
4582   DMPlexGetMeet - Get an array for the meet of the set of points
4583 
4584   Not Collective
4585 
4586   Input Parameters:
4587 + dm        - The `DMPLEX` object
4588 . numPoints - The number of input points for the meet
4589 - points    - The input points
4590 
4591   Output Parameters:
4592 + numCoveringPoints - The number of points in the meet
4593 - coveringPoints    - The points in the meet
4594 
4595   Level: intermediate
4596 
4597   Note:
4598   Currently, this is restricted to a single level meet
4599 
4600   Fortran Notes:
4601   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4602 
4603 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4604 @*/
4605 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4606 {
4607   DM_Plex  *mesh = (DM_Plex *)dm->data;
4608   PetscInt *meet[2];
4609   PetscInt  meetSize, i = 0;
4610   PetscInt  dof, off, p, c, m;
4611   PetscInt  maxConeSize;
4612 
4613   PetscFunctionBegin;
4614   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4615   PetscAssertPointer(points, 3);
4616   PetscAssertPointer(numCoveringPoints, 4);
4617   PetscAssertPointer(coveringPoints, 5);
4618   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4619   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4620   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4621   /* Copy in cone of first point */
4622   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4623   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4624   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4625   /* Check each successive cone */
4626   for (p = 1; p < numPoints; ++p) {
4627     PetscInt newMeetSize = 0;
4628 
4629     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4630     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4631     for (c = 0; c < dof; ++c) {
4632       const PetscInt point = mesh->cones[off + c];
4633 
4634       for (m = 0; m < meetSize; ++m) {
4635         if (point == meet[i][m]) {
4636           meet[1 - i][newMeetSize++] = point;
4637           break;
4638         }
4639       }
4640     }
4641     meetSize = newMeetSize;
4642     i        = 1 - i;
4643   }
4644   *numCoveringPoints = meetSize;
4645   *coveringPoints    = meet[i];
4646   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4647   PetscFunctionReturn(PETSC_SUCCESS);
4648 }
4649 
4650 /*@C
4651   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4652 
4653   Not Collective
4654 
4655   Input Parameters:
4656 + dm        - The `DMPLEX` object
4657 . numPoints - The number of input points for the meet
4658 - points    - The input points
4659 
4660   Output Parameters:
4661 + numCoveredPoints - The number of points in the meet
4662 - coveredPoints    - The points in the meet
4663 
4664   Level: intermediate
4665 
4666   Fortran Notes:
4667   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4668 
4669 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4670 @*/
4671 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4672 {
4673   PetscFunctionBegin;
4674   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4675   if (points) PetscAssertPointer(points, 3);
4676   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4677   PetscAssertPointer(coveredPoints, 5);
4678   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4679   if (numCoveredPoints) *numCoveredPoints = 0;
4680   PetscFunctionReturn(PETSC_SUCCESS);
4681 }
4682 
4683 /*@C
4684   DMPlexGetFullMeet - Get an array for the meet of the set of points
4685 
4686   Not Collective
4687 
4688   Input Parameters:
4689 + dm        - The `DMPLEX` object
4690 . numPoints - The number of input points for the meet
4691 - points    - The input points
4692 
4693   Output Parameters:
4694 + numCoveredPoints - The number of points in the meet
4695 - coveredPoints    - The points in the meet
4696 
4697   Level: intermediate
4698 
4699   Fortran Notes:
4700   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4701 
4702 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4703 @*/
4704 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4705 {
4706   PetscInt *offsets, **closures;
4707   PetscInt *meet[2];
4708   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4709   PetscInt  p, h, c, m, mc;
4710 
4711   PetscFunctionBegin;
4712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4713   PetscAssertPointer(points, 3);
4714   PetscAssertPointer(numCoveredPoints, 4);
4715   PetscAssertPointer(coveredPoints, 5);
4716 
4717   PetscCall(DMPlexGetDepth(dm, &height));
4718   PetscCall(PetscMalloc1(numPoints, &closures));
4719   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4720   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4721   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4722   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4723   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4724 
4725   for (p = 0; p < numPoints; ++p) {
4726     PetscInt closureSize;
4727 
4728     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4729 
4730     offsets[p * (height + 2) + 0] = 0;
4731     for (h = 0; h < height + 1; ++h) {
4732       PetscInt pStart, pEnd, i;
4733 
4734       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4735       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4736         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4737           offsets[p * (height + 2) + h + 1] = i;
4738           break;
4739         }
4740       }
4741       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4742     }
4743     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);
4744   }
4745   for (h = 0; h < height + 1; ++h) {
4746     PetscInt dof;
4747 
4748     /* Copy in cone of first point */
4749     dof = offsets[h + 1] - offsets[h];
4750     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4751     /* Check each successive cone */
4752     for (p = 1; p < numPoints && meetSize; ++p) {
4753       PetscInt newMeetSize = 0;
4754 
4755       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4756       for (c = 0; c < dof; ++c) {
4757         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4758 
4759         for (m = 0; m < meetSize; ++m) {
4760           if (point == meet[i][m]) {
4761             meet[1 - i][newMeetSize++] = point;
4762             break;
4763           }
4764         }
4765       }
4766       meetSize = newMeetSize;
4767       i        = 1 - i;
4768     }
4769     if (meetSize) break;
4770   }
4771   *numCoveredPoints = meetSize;
4772   *coveredPoints    = meet[i];
4773   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4774   PetscCall(PetscFree(closures));
4775   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4776   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4777   PetscFunctionReturn(PETSC_SUCCESS);
4778 }
4779 
4780 /*@C
4781   DMPlexEqual - Determine if two `DM` have the same topology
4782 
4783   Not Collective
4784 
4785   Input Parameters:
4786 + dmA - A `DMPLEX` object
4787 - dmB - A `DMPLEX` object
4788 
4789   Output Parameter:
4790 . equal - `PETSC_TRUE` if the topologies are identical
4791 
4792   Level: intermediate
4793 
4794   Note:
4795   We are not solving graph isomorphism, so we do not permute.
4796 
4797 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4798 @*/
4799 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4800 {
4801   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4802 
4803   PetscFunctionBegin;
4804   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4805   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4806   PetscAssertPointer(equal, 3);
4807 
4808   *equal = PETSC_FALSE;
4809   PetscCall(DMPlexGetDepth(dmA, &depth));
4810   PetscCall(DMPlexGetDepth(dmB, &depthB));
4811   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4812   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4813   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4814   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4815   for (p = pStart; p < pEnd; ++p) {
4816     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4817     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4818 
4819     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4820     PetscCall(DMPlexGetCone(dmA, p, &cone));
4821     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4822     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4823     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4824     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4825     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4826     for (c = 0; c < coneSize; ++c) {
4827       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4828       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4829     }
4830     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4831     PetscCall(DMPlexGetSupport(dmA, p, &support));
4832     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4833     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4834     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4835     for (s = 0; s < supportSize; ++s) {
4836       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4837     }
4838   }
4839   *equal = PETSC_TRUE;
4840   PetscFunctionReturn(PETSC_SUCCESS);
4841 }
4842 
4843 /*@C
4844   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4845 
4846   Not Collective
4847 
4848   Input Parameters:
4849 + dm         - The `DMPLEX`
4850 . cellDim    - The cell dimension
4851 - numCorners - The number of vertices on a cell
4852 
4853   Output Parameter:
4854 . numFaceVertices - The number of vertices on a face
4855 
4856   Level: developer
4857 
4858   Note:
4859   Of course this can only work for a restricted set of symmetric shapes
4860 
4861 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4862 @*/
4863 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4864 {
4865   MPI_Comm comm;
4866 
4867   PetscFunctionBegin;
4868   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4869   PetscAssertPointer(numFaceVertices, 4);
4870   switch (cellDim) {
4871   case 0:
4872     *numFaceVertices = 0;
4873     break;
4874   case 1:
4875     *numFaceVertices = 1;
4876     break;
4877   case 2:
4878     switch (numCorners) {
4879     case 3:                 /* triangle */
4880       *numFaceVertices = 2; /* Edge has 2 vertices */
4881       break;
4882     case 4:                 /* quadrilateral */
4883       *numFaceVertices = 2; /* Edge has 2 vertices */
4884       break;
4885     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4886       *numFaceVertices = 3; /* Edge has 3 vertices */
4887       break;
4888     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4889       *numFaceVertices = 3; /* Edge has 3 vertices */
4890       break;
4891     default:
4892       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4893     }
4894     break;
4895   case 3:
4896     switch (numCorners) {
4897     case 4:                 /* tetradehdron */
4898       *numFaceVertices = 3; /* Face has 3 vertices */
4899       break;
4900     case 6:                 /* tet cohesive cells */
4901       *numFaceVertices = 4; /* Face has 4 vertices */
4902       break;
4903     case 8:                 /* hexahedron */
4904       *numFaceVertices = 4; /* Face has 4 vertices */
4905       break;
4906     case 9:                 /* tet cohesive Lagrange cells */
4907       *numFaceVertices = 6; /* Face has 6 vertices */
4908       break;
4909     case 10:                /* quadratic tetrahedron */
4910       *numFaceVertices = 6; /* Face has 6 vertices */
4911       break;
4912     case 12:                /* hex cohesive Lagrange cells */
4913       *numFaceVertices = 6; /* Face has 6 vertices */
4914       break;
4915     case 18:                /* quadratic tet cohesive Lagrange cells */
4916       *numFaceVertices = 6; /* Face has 6 vertices */
4917       break;
4918     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4919       *numFaceVertices = 9; /* Face has 9 vertices */
4920       break;
4921     default:
4922       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4923     }
4924     break;
4925   default:
4926     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4927   }
4928   PetscFunctionReturn(PETSC_SUCCESS);
4929 }
4930 
4931 /*@
4932   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4933 
4934   Not Collective
4935 
4936   Input Parameter:
4937 . dm - The `DMPLEX` object
4938 
4939   Output Parameter:
4940 . depthLabel - The `DMLabel` recording point depth
4941 
4942   Level: developer
4943 
4944 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4945 @*/
4946 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4947 {
4948   PetscFunctionBegin;
4949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4950   PetscAssertPointer(depthLabel, 2);
4951   *depthLabel = dm->depthLabel;
4952   PetscFunctionReturn(PETSC_SUCCESS);
4953 }
4954 
4955 /*@
4956   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4957 
4958   Not Collective
4959 
4960   Input Parameter:
4961 . dm - The `DMPLEX` object
4962 
4963   Output Parameter:
4964 . depth - The number of strata (breadth first levels) in the DAG
4965 
4966   Level: developer
4967 
4968   Notes:
4969   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4970 
4971   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4972 
4973   An empty mesh gives -1.
4974 
4975 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4976 @*/
4977 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4978 {
4979   DM_Plex *mesh = (DM_Plex *)dm->data;
4980   DMLabel  label;
4981   PetscInt d = 0;
4982 
4983   PetscFunctionBegin;
4984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4985   PetscAssertPointer(depth, 2);
4986   if (mesh->tr) {
4987     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4988   } else {
4989     PetscCall(DMPlexGetDepthLabel(dm, &label));
4990     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4991     *depth = d - 1;
4992   }
4993   PetscFunctionReturn(PETSC_SUCCESS);
4994 }
4995 
4996 /*@
4997   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
4998 
4999   Not Collective
5000 
5001   Input Parameters:
5002 + dm    - The `DMPLEX` object
5003 - depth - The requested depth
5004 
5005   Output Parameters:
5006 + start - The first point at this `depth`
5007 - end   - One beyond the last point at this `depth`
5008 
5009   Level: developer
5010 
5011   Notes:
5012   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5013   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5014   higher dimension, e.g., "edges".
5015 
5016 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5017 @*/
5018 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5019 {
5020   DM_Plex *mesh = (DM_Plex *)dm->data;
5021   DMLabel  label;
5022   PetscInt pStart, pEnd;
5023 
5024   PetscFunctionBegin;
5025   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5026   if (start) {
5027     PetscAssertPointer(start, 3);
5028     *start = 0;
5029   }
5030   if (end) {
5031     PetscAssertPointer(end, 4);
5032     *end = 0;
5033   }
5034   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5035   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5036   if (depth < 0) {
5037     if (start) *start = pStart;
5038     if (end) *end = pEnd;
5039     PetscFunctionReturn(PETSC_SUCCESS);
5040   }
5041   if (mesh->tr) {
5042     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5043   } else {
5044     PetscCall(DMPlexGetDepthLabel(dm, &label));
5045     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5046     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5047   }
5048   PetscFunctionReturn(PETSC_SUCCESS);
5049 }
5050 
5051 /*@
5052   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5053 
5054   Not Collective
5055 
5056   Input Parameters:
5057 + dm     - The `DMPLEX` object
5058 - height - The requested height
5059 
5060   Output Parameters:
5061 + start - The first point at this `height`
5062 - end   - One beyond the last point at this `height`
5063 
5064   Level: developer
5065 
5066   Notes:
5067   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5068   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5069   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5070 
5071 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5072 @*/
5073 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5074 {
5075   DMLabel  label;
5076   PetscInt depth, pStart, pEnd;
5077 
5078   PetscFunctionBegin;
5079   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5080   if (start) {
5081     PetscAssertPointer(start, 3);
5082     *start = 0;
5083   }
5084   if (end) {
5085     PetscAssertPointer(end, 4);
5086     *end = 0;
5087   }
5088   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5089   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5090   if (height < 0) {
5091     if (start) *start = pStart;
5092     if (end) *end = pEnd;
5093     PetscFunctionReturn(PETSC_SUCCESS);
5094   }
5095   PetscCall(DMPlexGetDepthLabel(dm, &label));
5096   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5097   else PetscCall(DMGetDimension(dm, &depth));
5098   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5099   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5100   PetscFunctionReturn(PETSC_SUCCESS);
5101 }
5102 
5103 /*@
5104   DMPlexGetPointDepth - Get the `depth` of a given point
5105 
5106   Not Collective
5107 
5108   Input Parameters:
5109 + dm    - The `DMPLEX` object
5110 - point - The point
5111 
5112   Output Parameter:
5113 . depth - The depth of the `point`
5114 
5115   Level: intermediate
5116 
5117 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5118 @*/
5119 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5120 {
5121   PetscFunctionBegin;
5122   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5123   PetscAssertPointer(depth, 3);
5124   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5125   PetscFunctionReturn(PETSC_SUCCESS);
5126 }
5127 
5128 /*@
5129   DMPlexGetPointHeight - Get the `height` of a given point
5130 
5131   Not Collective
5132 
5133   Input Parameters:
5134 + dm    - The `DMPLEX` object
5135 - point - The point
5136 
5137   Output Parameter:
5138 . height - The height of the `point`
5139 
5140   Level: intermediate
5141 
5142 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5143 @*/
5144 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5145 {
5146   PetscInt n, pDepth;
5147 
5148   PetscFunctionBegin;
5149   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5150   PetscAssertPointer(height, 3);
5151   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5152   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5153   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5154   PetscFunctionReturn(PETSC_SUCCESS);
5155 }
5156 
5157 /*@
5158   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5159 
5160   Not Collective
5161 
5162   Input Parameter:
5163 . dm - The `DMPLEX` object
5164 
5165   Output Parameter:
5166 . celltypeLabel - The `DMLabel` recording cell polytope type
5167 
5168   Level: developer
5169 
5170   Note:
5171   This function will trigger automatica computation of cell types. This can be disabled by calling
5172   `DMCreateLabel`(dm, "celltype") beforehand.
5173 
5174 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5175 @*/
5176 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5177 {
5178   PetscFunctionBegin;
5179   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5180   PetscAssertPointer(celltypeLabel, 2);
5181   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5182   *celltypeLabel = dm->celltypeLabel;
5183   PetscFunctionReturn(PETSC_SUCCESS);
5184 }
5185 
5186 /*@
5187   DMPlexGetCellType - Get the polytope type of a given cell
5188 
5189   Not Collective
5190 
5191   Input Parameters:
5192 + dm   - The `DMPLEX` object
5193 - cell - The cell
5194 
5195   Output Parameter:
5196 . celltype - The polytope type of the cell
5197 
5198   Level: intermediate
5199 
5200 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5201 @*/
5202 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5203 {
5204   DM_Plex *mesh = (DM_Plex *)dm->data;
5205   DMLabel  label;
5206   PetscInt ct;
5207 
5208   PetscFunctionBegin;
5209   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5210   PetscAssertPointer(celltype, 3);
5211   if (mesh->tr) {
5212     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5213   } else {
5214     PetscInt pStart, pEnd;
5215 
5216     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5217     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5218       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5219       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5220       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5221       for (PetscInt p = pStart; p < pEnd; p++) {
5222         PetscCall(DMLabelGetValue(label, p, &ct));
5223         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5224       }
5225     }
5226     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5227     if (PetscDefined(USE_DEBUG)) {
5228       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5229       PetscCall(DMLabelGetValue(label, cell, &ct));
5230       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5231       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5232     }
5233   }
5234   PetscFunctionReturn(PETSC_SUCCESS);
5235 }
5236 
5237 /*@
5238   DMPlexSetCellType - Set the polytope type of a given cell
5239 
5240   Not Collective
5241 
5242   Input Parameters:
5243 + dm       - The `DMPLEX` object
5244 . cell     - The cell
5245 - celltype - The polytope type of the cell
5246 
5247   Level: advanced
5248 
5249   Note:
5250   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5251   is executed. This function will override the computed type. However, if automatic classification will not succeed
5252   and a user wants to manually specify all types, the classification must be disabled by calling
5253   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5254 
5255 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5256 @*/
5257 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5258 {
5259   DM_Plex *mesh = (DM_Plex *)dm->data;
5260   DMLabel  label;
5261   PetscInt pStart, pEnd;
5262 
5263   PetscFunctionBegin;
5264   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5265   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5266   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5267   PetscCall(DMLabelSetValue(label, cell, celltype));
5268   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5269   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5270   PetscFunctionReturn(PETSC_SUCCESS);
5271 }
5272 
5273 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5274 {
5275   PetscSection section, s;
5276   Mat          m;
5277   PetscInt     maxHeight;
5278   const char  *prefix;
5279 
5280   PetscFunctionBegin;
5281   PetscCall(DMClone(dm, cdm));
5282   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5283   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5284   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5285   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5286   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5287   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5288   PetscCall(DMSetLocalSection(*cdm, section));
5289   PetscCall(PetscSectionDestroy(&section));
5290   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5291   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5292   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5293   PetscCall(PetscSectionDestroy(&s));
5294   PetscCall(MatDestroy(&m));
5295 
5296   PetscCall(DMSetNumFields(*cdm, 1));
5297   PetscCall(DMCreateDS(*cdm));
5298   (*cdm)->cloneOpts = PETSC_TRUE;
5299   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5300   PetscFunctionReturn(PETSC_SUCCESS);
5301 }
5302 
5303 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5304 {
5305   Vec coordsLocal, cellCoordsLocal;
5306   DM  coordsDM, cellCoordsDM;
5307 
5308   PetscFunctionBegin;
5309   *field = NULL;
5310   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5311   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5312   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5313   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5314   if (coordsLocal && coordsDM) {
5315     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5316     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5317   }
5318   PetscFunctionReturn(PETSC_SUCCESS);
5319 }
5320 
5321 /*@C
5322   DMPlexGetConeSection - Return a section which describes the layout of cone data
5323 
5324   Not Collective
5325 
5326   Input Parameter:
5327 . dm - The `DMPLEX` object
5328 
5329   Output Parameter:
5330 . section - The `PetscSection` object
5331 
5332   Level: developer
5333 
5334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5335 @*/
5336 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5337 {
5338   DM_Plex *mesh = (DM_Plex *)dm->data;
5339 
5340   PetscFunctionBegin;
5341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5342   if (section) *section = mesh->coneSection;
5343   PetscFunctionReturn(PETSC_SUCCESS);
5344 }
5345 
5346 /*@C
5347   DMPlexGetSupportSection - Return a section which describes the layout of support data
5348 
5349   Not Collective
5350 
5351   Input Parameter:
5352 . dm - The `DMPLEX` object
5353 
5354   Output Parameter:
5355 . section - The `PetscSection` object
5356 
5357   Level: developer
5358 
5359 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5360 @*/
5361 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5362 {
5363   DM_Plex *mesh = (DM_Plex *)dm->data;
5364 
5365   PetscFunctionBegin;
5366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5367   if (section) *section = mesh->supportSection;
5368   PetscFunctionReturn(PETSC_SUCCESS);
5369 }
5370 
5371 /*@C
5372   DMPlexGetCones - Return cone data
5373 
5374   Not Collective
5375 
5376   Input Parameter:
5377 . dm - The `DMPLEX` object
5378 
5379   Output Parameter:
5380 . cones - The cone for each point
5381 
5382   Level: developer
5383 
5384 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5385 @*/
5386 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5387 {
5388   DM_Plex *mesh = (DM_Plex *)dm->data;
5389 
5390   PetscFunctionBegin;
5391   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5392   if (cones) *cones = mesh->cones;
5393   PetscFunctionReturn(PETSC_SUCCESS);
5394 }
5395 
5396 /*@C
5397   DMPlexGetConeOrientations - Return cone orientation data
5398 
5399   Not Collective
5400 
5401   Input Parameter:
5402 . dm - The `DMPLEX` object
5403 
5404   Output Parameter:
5405 . coneOrientations - The array of cone orientations for all points
5406 
5407   Level: developer
5408 
5409   Notes:
5410   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5411 
5412   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5413 
5414 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5415 @*/
5416 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5417 {
5418   DM_Plex *mesh = (DM_Plex *)dm->data;
5419 
5420   PetscFunctionBegin;
5421   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5422   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5423   PetscFunctionReturn(PETSC_SUCCESS);
5424 }
5425 
5426 /******************************** FEM Support **********************************/
5427 
5428 /*
5429  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5430  representing a line in the section.
5431 */
5432 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5433 {
5434   PetscFunctionBeginHot;
5435   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5436   if (line < 0) {
5437     *k  = 0;
5438     *Nc = 0;
5439   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5440     *k = 1;
5441   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5442     /* An order k SEM disc has k-1 dofs on an edge */
5443     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5444     *k = *k / *Nc + 1;
5445   }
5446   PetscFunctionReturn(PETSC_SUCCESS);
5447 }
5448 
5449 /*@
5450 
5451   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5452   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5453   section provided (or the section of the `DM`).
5454 
5455   Input Parameters:
5456 + dm      - The `DM`
5457 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5458 - section - The `PetscSection` to reorder, or `NULL` for the default section
5459 
5460   Example:
5461   A typical interpolated single-quad mesh might order points as
5462 .vb
5463   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5464 
5465   v4 -- e6 -- v3
5466   |           |
5467   e7    c0    e8
5468   |           |
5469   v1 -- e5 -- v2
5470 .ve
5471 
5472   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5473   dofs in the order of points, e.g.,
5474 .vb
5475     c0 -> [0,1,2,3]
5476     v1 -> [4]
5477     ...
5478     e5 -> [8, 9]
5479 .ve
5480 
5481   which corresponds to the dofs
5482 .vb
5483     6   10  11  7
5484     13  2   3   15
5485     12  0   1   14
5486     4   8   9   5
5487 .ve
5488 
5489   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5490 .vb
5491   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5492 .ve
5493 
5494   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5495 .vb
5496    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5497 .ve
5498 
5499   Level: developer
5500 
5501   Notes:
5502   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5503   degree of the basis.
5504 
5505   This is required to run with libCEED.
5506 
5507 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5508 @*/
5509 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5510 {
5511   DMLabel   label;
5512   PetscInt  dim, depth = -1, eStart = -1, Nf;
5513   PetscBool vertexchart;
5514 
5515   PetscFunctionBegin;
5516   PetscCall(DMGetDimension(dm, &dim));
5517   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5518   if (point < 0) {
5519     PetscInt sStart, sEnd;
5520 
5521     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5522     point = sEnd - sStart ? sStart : point;
5523   }
5524   PetscCall(DMPlexGetDepthLabel(dm, &label));
5525   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5526   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5527   if (depth == 1) {
5528     eStart = point;
5529   } else if (depth == dim) {
5530     const PetscInt *cone;
5531 
5532     PetscCall(DMPlexGetCone(dm, point, &cone));
5533     if (dim == 2) eStart = cone[0];
5534     else if (dim == 3) {
5535       const PetscInt *cone2;
5536       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5537       eStart = cone2[0];
5538     } 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);
5539   } 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);
5540   { /* Determine whether the chart covers all points or just vertices. */
5541     PetscInt pStart, pEnd, cStart, cEnd;
5542     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5543     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5544     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5545     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5546     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5547   }
5548   PetscCall(PetscSectionGetNumFields(section, &Nf));
5549   for (PetscInt d = 1; d <= dim; d++) {
5550     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5551     PetscInt *perm;
5552 
5553     for (f = 0; f < Nf; ++f) {
5554       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5555       size += PetscPowInt(k + 1, d) * Nc;
5556     }
5557     PetscCall(PetscMalloc1(size, &perm));
5558     for (f = 0; f < Nf; ++f) {
5559       switch (d) {
5560       case 1:
5561         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5562         /*
5563          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5564          We want              [ vtx0; edge of length k-1; vtx1 ]
5565          */
5566         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5567         for (i = 0; i < k - 1; i++)
5568           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5569         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5570         foffset = offset;
5571         break;
5572       case 2:
5573         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5574         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5575         /* The SEM order is
5576 
5577          v_lb, {e_b}, v_rb,
5578          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5579          v_lt, reverse {e_t}, v_rt
5580          */
5581         {
5582           const PetscInt of   = 0;
5583           const PetscInt oeb  = of + PetscSqr(k - 1);
5584           const PetscInt oer  = oeb + (k - 1);
5585           const PetscInt oet  = oer + (k - 1);
5586           const PetscInt oel  = oet + (k - 1);
5587           const PetscInt ovlb = oel + (k - 1);
5588           const PetscInt ovrb = ovlb + 1;
5589           const PetscInt ovrt = ovrb + 1;
5590           const PetscInt ovlt = ovrt + 1;
5591           PetscInt       o;
5592 
5593           /* bottom */
5594           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5595           for (o = oeb; o < oer; ++o)
5596             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5597           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5598           /* middle */
5599           for (i = 0; i < k - 1; ++i) {
5600             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5601             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5602               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5603             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5604           }
5605           /* top */
5606           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5607           for (o = oel - 1; o >= oet; --o)
5608             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5609           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5610           foffset = offset;
5611         }
5612         break;
5613       case 3:
5614         /* The original hex closure is
5615 
5616          {c,
5617          f_b, f_t, f_f, f_b, f_r, f_l,
5618          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5619          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5620          */
5621         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5622         /* The SEM order is
5623          Bottom Slice
5624          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5625          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5626          v_blb, {e_bb}, v_brb,
5627 
5628          Middle Slice (j)
5629          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5630          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5631          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5632 
5633          Top Slice
5634          v_tlf, {e_tf}, v_trf,
5635          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5636          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5637          */
5638         {
5639           const PetscInt oc    = 0;
5640           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5641           const PetscInt oft   = ofb + PetscSqr(k - 1);
5642           const PetscInt off   = oft + PetscSqr(k - 1);
5643           const PetscInt ofk   = off + PetscSqr(k - 1);
5644           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5645           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5646           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5647           const PetscInt oebb  = oebl + (k - 1);
5648           const PetscInt oebr  = oebb + (k - 1);
5649           const PetscInt oebf  = oebr + (k - 1);
5650           const PetscInt oetf  = oebf + (k - 1);
5651           const PetscInt oetr  = oetf + (k - 1);
5652           const PetscInt oetb  = oetr + (k - 1);
5653           const PetscInt oetl  = oetb + (k - 1);
5654           const PetscInt oerf  = oetl + (k - 1);
5655           const PetscInt oelf  = oerf + (k - 1);
5656           const PetscInt oelb  = oelf + (k - 1);
5657           const PetscInt oerb  = oelb + (k - 1);
5658           const PetscInt ovblf = oerb + (k - 1);
5659           const PetscInt ovblb = ovblf + 1;
5660           const PetscInt ovbrb = ovblb + 1;
5661           const PetscInt ovbrf = ovbrb + 1;
5662           const PetscInt ovtlf = ovbrf + 1;
5663           const PetscInt ovtrf = ovtlf + 1;
5664           const PetscInt ovtrb = ovtrf + 1;
5665           const PetscInt ovtlb = ovtrb + 1;
5666           PetscInt       o, n;
5667 
5668           /* Bottom Slice */
5669           /*   bottom */
5670           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5671           for (o = oetf - 1; o >= oebf; --o)
5672             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5673           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5674           /*   middle */
5675           for (i = 0; i < k - 1; ++i) {
5676             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5677             for (n = 0; n < k - 1; ++n) {
5678               o = ofb + n * (k - 1) + i;
5679               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5680             }
5681             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5682           }
5683           /*   top */
5684           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5685           for (o = oebb; o < oebr; ++o)
5686             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5687           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5688 
5689           /* Middle Slice */
5690           for (j = 0; j < k - 1; ++j) {
5691             /*   bottom */
5692             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5693             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5694               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5695             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5696             /*   middle */
5697             for (i = 0; i < k - 1; ++i) {
5698               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5699               for (n = 0; n < k - 1; ++n)
5700                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5701               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5702             }
5703             /*   top */
5704             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5705             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5706               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5707             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5708           }
5709 
5710           /* Top Slice */
5711           /*   bottom */
5712           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5713           for (o = oetf; o < oetr; ++o)
5714             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5715           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5716           /*   middle */
5717           for (i = 0; i < k - 1; ++i) {
5718             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5719             for (n = 0; n < k - 1; ++n)
5720               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5721             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5722           }
5723           /*   top */
5724           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5725           for (o = oetl - 1; o >= oetb; --o)
5726             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5727           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5728 
5729           foffset = offset;
5730         }
5731         break;
5732       default:
5733         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5734       }
5735     }
5736     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5737     /* Check permutation */
5738     {
5739       PetscInt *check;
5740 
5741       PetscCall(PetscMalloc1(size, &check));
5742       for (i = 0; i < size; ++i) {
5743         check[i] = -1;
5744         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5745       }
5746       for (i = 0; i < size; ++i) check[perm[i]] = i;
5747       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5748       PetscCall(PetscFree(check));
5749     }
5750     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5751     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5752       PetscInt *loc_perm;
5753       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5754       for (PetscInt i = 0; i < size; i++) {
5755         loc_perm[i]        = perm[i];
5756         loc_perm[size + i] = size + perm[i];
5757       }
5758       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5759     }
5760   }
5761   PetscFunctionReturn(PETSC_SUCCESS);
5762 }
5763 
5764 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5765 {
5766   PetscDS  prob;
5767   PetscInt depth, Nf, h;
5768   DMLabel  label;
5769 
5770   PetscFunctionBeginHot;
5771   PetscCall(DMGetDS(dm, &prob));
5772   Nf      = prob->Nf;
5773   label   = dm->depthLabel;
5774   *dspace = NULL;
5775   if (field < Nf) {
5776     PetscObject disc = prob->disc[field];
5777 
5778     if (disc->classid == PETSCFE_CLASSID) {
5779       PetscDualSpace dsp;
5780 
5781       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5782       PetscCall(DMLabelGetNumValues(label, &depth));
5783       PetscCall(DMLabelGetValue(label, point, &h));
5784       h = depth - 1 - h;
5785       if (h) {
5786         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5787       } else {
5788         *dspace = dsp;
5789       }
5790     }
5791   }
5792   PetscFunctionReturn(PETSC_SUCCESS);
5793 }
5794 
5795 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5796 {
5797   PetscScalar       *array;
5798   const PetscScalar *vArray;
5799   const PetscInt    *cone, *coneO;
5800   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5801 
5802   PetscFunctionBeginHot;
5803   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5804   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5805   PetscCall(DMPlexGetCone(dm, point, &cone));
5806   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5807   if (!values || !*values) {
5808     if ((point >= pStart) && (point < pEnd)) {
5809       PetscInt dof;
5810 
5811       PetscCall(PetscSectionGetDof(section, point, &dof));
5812       size += dof;
5813     }
5814     for (p = 0; p < numPoints; ++p) {
5815       const PetscInt cp = cone[p];
5816       PetscInt       dof;
5817 
5818       if ((cp < pStart) || (cp >= pEnd)) continue;
5819       PetscCall(PetscSectionGetDof(section, cp, &dof));
5820       size += dof;
5821     }
5822     if (!values) {
5823       if (csize) *csize = size;
5824       PetscFunctionReturn(PETSC_SUCCESS);
5825     }
5826     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5827   } else {
5828     array = *values;
5829   }
5830   size = 0;
5831   PetscCall(VecGetArrayRead(v, &vArray));
5832   if ((point >= pStart) && (point < pEnd)) {
5833     PetscInt           dof, off, d;
5834     const PetscScalar *varr;
5835 
5836     PetscCall(PetscSectionGetDof(section, point, &dof));
5837     PetscCall(PetscSectionGetOffset(section, point, &off));
5838     varr = &vArray[off];
5839     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5840     size += dof;
5841   }
5842   for (p = 0; p < numPoints; ++p) {
5843     const PetscInt     cp = cone[p];
5844     PetscInt           o  = coneO[p];
5845     PetscInt           dof, off, d;
5846     const PetscScalar *varr;
5847 
5848     if ((cp < pStart) || (cp >= pEnd)) continue;
5849     PetscCall(PetscSectionGetDof(section, cp, &dof));
5850     PetscCall(PetscSectionGetOffset(section, cp, &off));
5851     varr = &vArray[off];
5852     if (o >= 0) {
5853       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5854     } else {
5855       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5856     }
5857     size += dof;
5858   }
5859   PetscCall(VecRestoreArrayRead(v, &vArray));
5860   if (!*values) {
5861     if (csize) *csize = size;
5862     *values = array;
5863   } else {
5864     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5865     *csize = size;
5866   }
5867   PetscFunctionReturn(PETSC_SUCCESS);
5868 }
5869 
5870 /* Compress out points not in the section */
5871 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5872 {
5873   const PetscInt np = *numPoints;
5874   PetscInt       pStart, pEnd, p, q;
5875 
5876   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5877   for (p = 0, q = 0; p < np; ++p) {
5878     const PetscInt r = points[p * 2];
5879     if ((r >= pStart) && (r < pEnd)) {
5880       points[q * 2]     = r;
5881       points[q * 2 + 1] = points[p * 2 + 1];
5882       ++q;
5883     }
5884   }
5885   *numPoints = q;
5886   return PETSC_SUCCESS;
5887 }
5888 
5889 /* Compressed closure does not apply closure permutation */
5890 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5891 {
5892   const PetscInt *cla = NULL;
5893   PetscInt        np, *pts = NULL;
5894 
5895   PetscFunctionBeginHot;
5896   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5897   if (!ornt && *clPoints) {
5898     PetscInt dof, off;
5899 
5900     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5901     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5902     PetscCall(ISGetIndices(*clPoints, &cla));
5903     np  = dof / 2;
5904     pts = (PetscInt *)&cla[off];
5905   } else {
5906     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
5907     PetscCall(CompressPoints_Private(section, &np, pts));
5908   }
5909   *numPoints = np;
5910   *points    = pts;
5911   *clp       = cla;
5912   PetscFunctionReturn(PETSC_SUCCESS);
5913 }
5914 
5915 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5916 {
5917   PetscFunctionBeginHot;
5918   if (!*clPoints) {
5919     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5920   } else {
5921     PetscCall(ISRestoreIndices(*clPoints, clp));
5922   }
5923   *numPoints = 0;
5924   *points    = NULL;
5925   *clSec     = NULL;
5926   *clPoints  = NULL;
5927   *clp       = NULL;
5928   PetscFunctionReturn(PETSC_SUCCESS);
5929 }
5930 
5931 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5932 {
5933   PetscInt            offset = 0, p;
5934   const PetscInt    **perms  = NULL;
5935   const PetscScalar **flips  = NULL;
5936 
5937   PetscFunctionBeginHot;
5938   *size = 0;
5939   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5940   for (p = 0; p < numPoints; p++) {
5941     const PetscInt     point = points[2 * p];
5942     const PetscInt    *perm  = perms ? perms[p] : NULL;
5943     const PetscScalar *flip  = flips ? flips[p] : NULL;
5944     PetscInt           dof, off, d;
5945     const PetscScalar *varr;
5946 
5947     PetscCall(PetscSectionGetDof(section, point, &dof));
5948     PetscCall(PetscSectionGetOffset(section, point, &off));
5949     varr = &vArray[off];
5950     if (clperm) {
5951       if (perm) {
5952         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5953       } else {
5954         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5955       }
5956       if (flip) {
5957         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5958       }
5959     } else {
5960       if (perm) {
5961         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5962       } else {
5963         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5964       }
5965       if (flip) {
5966         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5967       }
5968     }
5969     offset += dof;
5970   }
5971   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5972   *size = offset;
5973   PetscFunctionReturn(PETSC_SUCCESS);
5974 }
5975 
5976 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[])
5977 {
5978   PetscInt offset = 0, f;
5979 
5980   PetscFunctionBeginHot;
5981   *size = 0;
5982   for (f = 0; f < numFields; ++f) {
5983     PetscInt            p;
5984     const PetscInt    **perms = NULL;
5985     const PetscScalar **flips = NULL;
5986 
5987     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5988     for (p = 0; p < numPoints; p++) {
5989       const PetscInt     point = points[2 * p];
5990       PetscInt           fdof, foff, b;
5991       const PetscScalar *varr;
5992       const PetscInt    *perm = perms ? perms[p] : NULL;
5993       const PetscScalar *flip = flips ? flips[p] : NULL;
5994 
5995       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5996       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5997       varr = &vArray[foff];
5998       if (clperm) {
5999         if (perm) {
6000           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6001         } else {
6002           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6003         }
6004         if (flip) {
6005           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6006         }
6007       } else {
6008         if (perm) {
6009           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6010         } else {
6011           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6012         }
6013         if (flip) {
6014           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6015         }
6016       }
6017       offset += fdof;
6018     }
6019     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6020   }
6021   *size = offset;
6022   PetscFunctionReturn(PETSC_SUCCESS);
6023 }
6024 
6025 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6026 {
6027   PetscSection    clSection;
6028   IS              clPoints;
6029   PetscInt       *points = NULL;
6030   const PetscInt *clp, *perm;
6031   PetscInt        depth, numFields, numPoints, asize;
6032 
6033   PetscFunctionBeginHot;
6034   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6035   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6036   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6037   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6038   PetscCall(DMPlexGetDepth(dm, &depth));
6039   PetscCall(PetscSectionGetNumFields(section, &numFields));
6040   if (depth == 1 && numFields < 2) {
6041     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6042     PetscFunctionReturn(PETSC_SUCCESS);
6043   }
6044   /* Get points */
6045   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6046   /* Get sizes */
6047   asize = 0;
6048   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6049     PetscInt dof;
6050     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6051     asize += dof;
6052   }
6053   if (values) {
6054     const PetscScalar *vArray;
6055     PetscInt           size;
6056 
6057     if (*values) {
6058       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);
6059     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6060     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6061     PetscCall(VecGetArrayRead(v, &vArray));
6062     /* Get values */
6063     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6064     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6065     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6066     /* Cleanup array */
6067     PetscCall(VecRestoreArrayRead(v, &vArray));
6068   }
6069   if (csize) *csize = asize;
6070   /* Cleanup points */
6071   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6072   PetscFunctionReturn(PETSC_SUCCESS);
6073 }
6074 
6075 /*@C
6076   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6077 
6078   Not collective
6079 
6080   Input Parameters:
6081 + dm      - The `DM`
6082 . section - The section describing the layout in `v`, or `NULL` to use the default section
6083 . v       - The local vector
6084 - point   - The point in the `DM`
6085 
6086   Input/Output Parameters:
6087 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6088 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6089            if the user provided `NULL`, it is a borrowed array and should not be freed
6090 
6091   Level: intermediate
6092 
6093   Notes:
6094   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6095   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6096   assembly function, and a user may already have allocated storage for this operation.
6097 
6098   A typical use could be
6099 .vb
6100    values = NULL;
6101    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6102    for (cl = 0; cl < clSize; ++cl) {
6103      <Compute on closure>
6104    }
6105    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6106 .ve
6107   or
6108 .vb
6109    PetscMalloc1(clMaxSize, &values);
6110    for (p = pStart; p < pEnd; ++p) {
6111      clSize = clMaxSize;
6112      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6113      for (cl = 0; cl < clSize; ++cl) {
6114        <Compute on closure>
6115      }
6116    }
6117    PetscFree(values);
6118 .ve
6119 
6120   Fortran Notes:
6121   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6122 
6123 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6124 @*/
6125 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6126 {
6127   PetscFunctionBeginHot;
6128   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, v, point, 0, csize, values));
6129   PetscFunctionReturn(PETSC_SUCCESS);
6130 }
6131 
6132 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6133 {
6134   DMLabel            depthLabel;
6135   PetscSection       clSection;
6136   IS                 clPoints;
6137   PetscScalar       *array;
6138   const PetscScalar *vArray;
6139   PetscInt          *points = NULL;
6140   const PetscInt    *clp, *perm = NULL;
6141   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6142 
6143   PetscFunctionBeginHot;
6144   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6145   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6146   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6147   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6148   PetscCall(DMPlexGetDepth(dm, &mdepth));
6149   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6150   PetscCall(PetscSectionGetNumFields(section, &numFields));
6151   if (mdepth == 1 && numFields < 2) {
6152     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6153     PetscFunctionReturn(PETSC_SUCCESS);
6154   }
6155   /* Get points */
6156   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6157   for (clsize = 0, p = 0; p < Np; p++) {
6158     PetscInt dof;
6159     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6160     clsize += dof;
6161   }
6162   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6163   /* Filter points */
6164   for (p = 0; p < numPoints * 2; p += 2) {
6165     PetscInt dep;
6166 
6167     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6168     if (dep != depth) continue;
6169     points[Np * 2 + 0] = points[p];
6170     points[Np * 2 + 1] = points[p + 1];
6171     ++Np;
6172   }
6173   /* Get array */
6174   if (!values || !*values) {
6175     PetscInt asize = 0, dof;
6176 
6177     for (p = 0; p < Np * 2; p += 2) {
6178       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6179       asize += dof;
6180     }
6181     if (!values) {
6182       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6183       if (csize) *csize = asize;
6184       PetscFunctionReturn(PETSC_SUCCESS);
6185     }
6186     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6187   } else {
6188     array = *values;
6189   }
6190   PetscCall(VecGetArrayRead(v, &vArray));
6191   /* Get values */
6192   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6193   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6194   /* Cleanup points */
6195   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6196   /* Cleanup array */
6197   PetscCall(VecRestoreArrayRead(v, &vArray));
6198   if (!*values) {
6199     if (csize) *csize = size;
6200     *values = array;
6201   } else {
6202     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6203     *csize = size;
6204   }
6205   PetscFunctionReturn(PETSC_SUCCESS);
6206 }
6207 
6208 /*@C
6209   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6210 
6211   Not collective
6212 
6213   Input Parameters:
6214 + dm      - The `DM`
6215 . section - The section describing the layout in `v`, or `NULL` to use the default section
6216 . v       - The local vector
6217 . point   - The point in the `DM`
6218 . csize   - The number of values in the closure, or `NULL`
6219 - values  - The array of values, which is a borrowed array and should not be freed
6220 
6221   Level: intermediate
6222 
6223   Note:
6224   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6225 
6226   Fortran Notes:
6227   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6228 
6229 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6230 @*/
6231 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6232 {
6233   PetscInt size = 0;
6234 
6235   PetscFunctionBegin;
6236   /* Should work without recalculating size */
6237   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6238   *values = NULL;
6239   PetscFunctionReturn(PETSC_SUCCESS);
6240 }
6241 
6242 static inline void add(PetscScalar *x, PetscScalar y)
6243 {
6244   *x += y;
6245 }
6246 static inline void insert(PetscScalar *x, PetscScalar y)
6247 {
6248   *x = y;
6249 }
6250 
6251 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[])
6252 {
6253   PetscInt        cdof;  /* The number of constraints on this point */
6254   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6255   PetscScalar    *a;
6256   PetscInt        off, cind = 0, k;
6257 
6258   PetscFunctionBegin;
6259   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6260   PetscCall(PetscSectionGetOffset(section, point, &off));
6261   a = &array[off];
6262   if (!cdof || setBC) {
6263     if (clperm) {
6264       if (perm) {
6265         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6266       } else {
6267         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6268       }
6269     } else {
6270       if (perm) {
6271         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6272       } else {
6273         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6274       }
6275     }
6276   } else {
6277     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6278     if (clperm) {
6279       if (perm) {
6280         for (k = 0; k < dof; ++k) {
6281           if ((cind < cdof) && (k == cdofs[cind])) {
6282             ++cind;
6283             continue;
6284           }
6285           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6286         }
6287       } else {
6288         for (k = 0; k < dof; ++k) {
6289           if ((cind < cdof) && (k == cdofs[cind])) {
6290             ++cind;
6291             continue;
6292           }
6293           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6294         }
6295       }
6296     } else {
6297       if (perm) {
6298         for (k = 0; k < dof; ++k) {
6299           if ((cind < cdof) && (k == cdofs[cind])) {
6300             ++cind;
6301             continue;
6302           }
6303           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6304         }
6305       } else {
6306         for (k = 0; k < dof; ++k) {
6307           if ((cind < cdof) && (k == cdofs[cind])) {
6308             ++cind;
6309             continue;
6310           }
6311           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6312         }
6313       }
6314     }
6315   }
6316   PetscFunctionReturn(PETSC_SUCCESS);
6317 }
6318 
6319 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[])
6320 {
6321   PetscInt        cdof;  /* The number of constraints on this point */
6322   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6323   PetscScalar    *a;
6324   PetscInt        off, cind = 0, k;
6325 
6326   PetscFunctionBegin;
6327   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6328   PetscCall(PetscSectionGetOffset(section, point, &off));
6329   a = &array[off];
6330   if (cdof) {
6331     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6332     if (clperm) {
6333       if (perm) {
6334         for (k = 0; k < dof; ++k) {
6335           if ((cind < cdof) && (k == cdofs[cind])) {
6336             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6337             cind++;
6338           }
6339         }
6340       } else {
6341         for (k = 0; k < dof; ++k) {
6342           if ((cind < cdof) && (k == cdofs[cind])) {
6343             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6344             cind++;
6345           }
6346         }
6347       }
6348     } else {
6349       if (perm) {
6350         for (k = 0; k < dof; ++k) {
6351           if ((cind < cdof) && (k == cdofs[cind])) {
6352             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6353             cind++;
6354           }
6355         }
6356       } else {
6357         for (k = 0; k < dof; ++k) {
6358           if ((cind < cdof) && (k == cdofs[cind])) {
6359             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6360             cind++;
6361           }
6362         }
6363       }
6364     }
6365   }
6366   PetscFunctionReturn(PETSC_SUCCESS);
6367 }
6368 
6369 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[])
6370 {
6371   PetscScalar    *a;
6372   PetscInt        fdof, foff, fcdof, foffset = *offset;
6373   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6374   PetscInt        cind = 0, b;
6375 
6376   PetscFunctionBegin;
6377   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6378   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6379   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6380   a = &array[foff];
6381   if (!fcdof || setBC) {
6382     if (clperm) {
6383       if (perm) {
6384         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6385       } else {
6386         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6387       }
6388     } else {
6389       if (perm) {
6390         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6391       } else {
6392         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6393       }
6394     }
6395   } else {
6396     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6397     if (clperm) {
6398       if (perm) {
6399         for (b = 0; b < fdof; b++) {
6400           if ((cind < fcdof) && (b == fcdofs[cind])) {
6401             ++cind;
6402             continue;
6403           }
6404           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6405         }
6406       } else {
6407         for (b = 0; b < fdof; b++) {
6408           if ((cind < fcdof) && (b == fcdofs[cind])) {
6409             ++cind;
6410             continue;
6411           }
6412           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6413         }
6414       }
6415     } else {
6416       if (perm) {
6417         for (b = 0; b < fdof; b++) {
6418           if ((cind < fcdof) && (b == fcdofs[cind])) {
6419             ++cind;
6420             continue;
6421           }
6422           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6423         }
6424       } else {
6425         for (b = 0; b < fdof; b++) {
6426           if ((cind < fcdof) && (b == fcdofs[cind])) {
6427             ++cind;
6428             continue;
6429           }
6430           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6431         }
6432       }
6433     }
6434   }
6435   *offset += fdof;
6436   PetscFunctionReturn(PETSC_SUCCESS);
6437 }
6438 
6439 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[])
6440 {
6441   PetscScalar    *a;
6442   PetscInt        fdof, foff, fcdof, foffset = *offset;
6443   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6444   PetscInt        Nc, cind = 0, ncind = 0, b;
6445   PetscBool       ncSet, fcSet;
6446 
6447   PetscFunctionBegin;
6448   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6449   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6450   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6451   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6452   a = &array[foff];
6453   if (fcdof) {
6454     /* We just override fcdof and fcdofs with Ncc and comps */
6455     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6456     if (clperm) {
6457       if (perm) {
6458         if (comps) {
6459           for (b = 0; b < fdof; b++) {
6460             ncSet = fcSet = PETSC_FALSE;
6461             if (b % Nc == comps[ncind]) {
6462               ncind = (ncind + 1) % Ncc;
6463               ncSet = PETSC_TRUE;
6464             }
6465             if ((cind < fcdof) && (b == fcdofs[cind])) {
6466               ++cind;
6467               fcSet = PETSC_TRUE;
6468             }
6469             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6470           }
6471         } else {
6472           for (b = 0; b < fdof; b++) {
6473             if ((cind < fcdof) && (b == fcdofs[cind])) {
6474               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6475               ++cind;
6476             }
6477           }
6478         }
6479       } else {
6480         if (comps) {
6481           for (b = 0; b < fdof; b++) {
6482             ncSet = fcSet = PETSC_FALSE;
6483             if (b % Nc == comps[ncind]) {
6484               ncind = (ncind + 1) % Ncc;
6485               ncSet = PETSC_TRUE;
6486             }
6487             if ((cind < fcdof) && (b == fcdofs[cind])) {
6488               ++cind;
6489               fcSet = PETSC_TRUE;
6490             }
6491             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6492           }
6493         } else {
6494           for (b = 0; b < fdof; b++) {
6495             if ((cind < fcdof) && (b == fcdofs[cind])) {
6496               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6497               ++cind;
6498             }
6499           }
6500         }
6501       }
6502     } else {
6503       if (perm) {
6504         if (comps) {
6505           for (b = 0; b < fdof; b++) {
6506             ncSet = fcSet = PETSC_FALSE;
6507             if (b % Nc == comps[ncind]) {
6508               ncind = (ncind + 1) % Ncc;
6509               ncSet = PETSC_TRUE;
6510             }
6511             if ((cind < fcdof) && (b == fcdofs[cind])) {
6512               ++cind;
6513               fcSet = PETSC_TRUE;
6514             }
6515             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6516           }
6517         } else {
6518           for (b = 0; b < fdof; b++) {
6519             if ((cind < fcdof) && (b == fcdofs[cind])) {
6520               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6521               ++cind;
6522             }
6523           }
6524         }
6525       } else {
6526         if (comps) {
6527           for (b = 0; b < fdof; b++) {
6528             ncSet = fcSet = PETSC_FALSE;
6529             if (b % Nc == comps[ncind]) {
6530               ncind = (ncind + 1) % Ncc;
6531               ncSet = PETSC_TRUE;
6532             }
6533             if ((cind < fcdof) && (b == fcdofs[cind])) {
6534               ++cind;
6535               fcSet = PETSC_TRUE;
6536             }
6537             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6538           }
6539         } else {
6540           for (b = 0; b < fdof; b++) {
6541             if ((cind < fcdof) && (b == fcdofs[cind])) {
6542               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6543               ++cind;
6544             }
6545           }
6546         }
6547       }
6548     }
6549   }
6550   *offset += fdof;
6551   PetscFunctionReturn(PETSC_SUCCESS);
6552 }
6553 
6554 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6555 {
6556   PetscScalar    *array;
6557   const PetscInt *cone, *coneO;
6558   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6559 
6560   PetscFunctionBeginHot;
6561   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6562   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6563   PetscCall(DMPlexGetCone(dm, point, &cone));
6564   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6565   PetscCall(VecGetArray(v, &array));
6566   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6567     const PetscInt cp = !p ? point : cone[p - 1];
6568     const PetscInt o  = !p ? 0 : coneO[p - 1];
6569 
6570     if ((cp < pStart) || (cp >= pEnd)) {
6571       dof = 0;
6572       continue;
6573     }
6574     PetscCall(PetscSectionGetDof(section, cp, &dof));
6575     /* ADD_VALUES */
6576     {
6577       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6578       PetscScalar    *a;
6579       PetscInt        cdof, coff, cind = 0, k;
6580 
6581       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6582       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6583       a = &array[coff];
6584       if (!cdof) {
6585         if (o >= 0) {
6586           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6587         } else {
6588           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6589         }
6590       } else {
6591         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6592         if (o >= 0) {
6593           for (k = 0; k < dof; ++k) {
6594             if ((cind < cdof) && (k == cdofs[cind])) {
6595               ++cind;
6596               continue;
6597             }
6598             a[k] += values[off + k];
6599           }
6600         } else {
6601           for (k = 0; k < dof; ++k) {
6602             if ((cind < cdof) && (k == cdofs[cind])) {
6603               ++cind;
6604               continue;
6605             }
6606             a[k] += values[off + dof - k - 1];
6607           }
6608         }
6609       }
6610     }
6611   }
6612   PetscCall(VecRestoreArray(v, &array));
6613   PetscFunctionReturn(PETSC_SUCCESS);
6614 }
6615 
6616 /*@C
6617   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6618 
6619   Not collective
6620 
6621   Input Parameters:
6622 + dm      - The `DM`
6623 . section - The section describing the layout in `v`, or `NULL` to use the default section
6624 . v       - The local vector
6625 . point   - The point in the `DM`
6626 . values  - The array of values
6627 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6628          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6629 
6630   Level: intermediate
6631 
6632 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6633 @*/
6634 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6635 {
6636   PetscSection    clSection;
6637   IS              clPoints;
6638   PetscScalar    *array;
6639   PetscInt       *points = NULL;
6640   const PetscInt *clp, *clperm = NULL;
6641   PetscInt        depth, numFields, numPoints, p, clsize;
6642 
6643   PetscFunctionBeginHot;
6644   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6645   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6646   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6647   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6648   PetscCall(DMPlexGetDepth(dm, &depth));
6649   PetscCall(PetscSectionGetNumFields(section, &numFields));
6650   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6651     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6652     PetscFunctionReturn(PETSC_SUCCESS);
6653   }
6654   /* Get points */
6655   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6656   for (clsize = 0, p = 0; p < numPoints; p++) {
6657     PetscInt dof;
6658     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6659     clsize += dof;
6660   }
6661   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6662   /* Get array */
6663   PetscCall(VecGetArray(v, &array));
6664   /* Get values */
6665   if (numFields > 0) {
6666     PetscInt offset = 0, f;
6667     for (f = 0; f < numFields; ++f) {
6668       const PetscInt    **perms = NULL;
6669       const PetscScalar **flips = NULL;
6670 
6671       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6672       switch (mode) {
6673       case INSERT_VALUES:
6674         for (p = 0; p < numPoints; p++) {
6675           const PetscInt     point = points[2 * p];
6676           const PetscInt    *perm  = perms ? perms[p] : NULL;
6677           const PetscScalar *flip  = flips ? flips[p] : NULL;
6678           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6679         }
6680         break;
6681       case INSERT_ALL_VALUES:
6682         for (p = 0; p < numPoints; p++) {
6683           const PetscInt     point = points[2 * p];
6684           const PetscInt    *perm  = perms ? perms[p] : NULL;
6685           const PetscScalar *flip  = flips ? flips[p] : NULL;
6686           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6687         }
6688         break;
6689       case INSERT_BC_VALUES:
6690         for (p = 0; p < numPoints; p++) {
6691           const PetscInt     point = points[2 * p];
6692           const PetscInt    *perm  = perms ? perms[p] : NULL;
6693           const PetscScalar *flip  = flips ? flips[p] : NULL;
6694           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6695         }
6696         break;
6697       case ADD_VALUES:
6698         for (p = 0; p < numPoints; p++) {
6699           const PetscInt     point = points[2 * p];
6700           const PetscInt    *perm  = perms ? perms[p] : NULL;
6701           const PetscScalar *flip  = flips ? flips[p] : NULL;
6702           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6703         }
6704         break;
6705       case ADD_ALL_VALUES:
6706         for (p = 0; p < numPoints; p++) {
6707           const PetscInt     point = points[2 * p];
6708           const PetscInt    *perm  = perms ? perms[p] : NULL;
6709           const PetscScalar *flip  = flips ? flips[p] : NULL;
6710           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6711         }
6712         break;
6713       case ADD_BC_VALUES:
6714         for (p = 0; p < numPoints; p++) {
6715           const PetscInt     point = points[2 * p];
6716           const PetscInt    *perm  = perms ? perms[p] : NULL;
6717           const PetscScalar *flip  = flips ? flips[p] : NULL;
6718           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6719         }
6720         break;
6721       default:
6722         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6723       }
6724       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6725     }
6726   } else {
6727     PetscInt            dof, off;
6728     const PetscInt    **perms = NULL;
6729     const PetscScalar **flips = NULL;
6730 
6731     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6732     switch (mode) {
6733     case INSERT_VALUES:
6734       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6735         const PetscInt     point = points[2 * p];
6736         const PetscInt    *perm  = perms ? perms[p] : NULL;
6737         const PetscScalar *flip  = flips ? flips[p] : NULL;
6738         PetscCall(PetscSectionGetDof(section, point, &dof));
6739         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6740       }
6741       break;
6742     case INSERT_ALL_VALUES:
6743       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6744         const PetscInt     point = points[2 * p];
6745         const PetscInt    *perm  = perms ? perms[p] : NULL;
6746         const PetscScalar *flip  = flips ? flips[p] : NULL;
6747         PetscCall(PetscSectionGetDof(section, point, &dof));
6748         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6749       }
6750       break;
6751     case INSERT_BC_VALUES:
6752       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6753         const PetscInt     point = points[2 * p];
6754         const PetscInt    *perm  = perms ? perms[p] : NULL;
6755         const PetscScalar *flip  = flips ? flips[p] : NULL;
6756         PetscCall(PetscSectionGetDof(section, point, &dof));
6757         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6758       }
6759       break;
6760     case ADD_VALUES:
6761       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6762         const PetscInt     point = points[2 * p];
6763         const PetscInt    *perm  = perms ? perms[p] : NULL;
6764         const PetscScalar *flip  = flips ? flips[p] : NULL;
6765         PetscCall(PetscSectionGetDof(section, point, &dof));
6766         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6767       }
6768       break;
6769     case ADD_ALL_VALUES:
6770       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6771         const PetscInt     point = points[2 * p];
6772         const PetscInt    *perm  = perms ? perms[p] : NULL;
6773         const PetscScalar *flip  = flips ? flips[p] : NULL;
6774         PetscCall(PetscSectionGetDof(section, point, &dof));
6775         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6776       }
6777       break;
6778     case ADD_BC_VALUES:
6779       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6780         const PetscInt     point = points[2 * p];
6781         const PetscInt    *perm  = perms ? perms[p] : NULL;
6782         const PetscScalar *flip  = flips ? flips[p] : NULL;
6783         PetscCall(PetscSectionGetDof(section, point, &dof));
6784         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6785       }
6786       break;
6787     default:
6788       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6789     }
6790     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6791   }
6792   /* Cleanup points */
6793   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6794   /* Cleanup array */
6795   PetscCall(VecRestoreArray(v, &array));
6796   PetscFunctionReturn(PETSC_SUCCESS);
6797 }
6798 
6799 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6800 {
6801   const PetscInt *supp, *cone;
6802   PetscScalar    *a;
6803   PetscInt        dim, Ns, dof, off, n = 0;
6804 
6805   PetscFunctionBegin;
6806   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6807   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6808   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6809   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6810   if (PetscDefined(USE_DEBUG)) {
6811     PetscInt vStart, vEnd;
6812 
6813     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6814     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);
6815   }
6816   PetscAssertPointer(values, 5);
6817 
6818   PetscCall(DMGetDimension(dm, &dim));
6819   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6820   PetscCall(DMPlexGetSupport(dm, point, &supp));
6821   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);
6822   PetscCall(VecGetArray(v, &a));
6823   PetscCall(PetscSectionGetDof(section, point, &dof));
6824   PetscCall(PetscSectionGetOffset(section, point, &off));
6825   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6826   for (PetscInt d = 0; d < dim; ++d) {
6827     // Left edge
6828     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6829     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6830     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6831     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6832     // Right edge
6833     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6834     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6835     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6836     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6837   }
6838   PetscCall(VecRestoreArray(v, &a));
6839   PetscFunctionReturn(PETSC_SUCCESS);
6840 }
6841 
6842 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6843 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6844 {
6845   PetscFunctionBegin;
6846   *contains = PETSC_TRUE;
6847   if (label) {
6848     PetscInt fdof;
6849 
6850     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6851     if (!*contains) {
6852       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6853       *offset += fdof;
6854       PetscFunctionReturn(PETSC_SUCCESS);
6855     }
6856   }
6857   PetscFunctionReturn(PETSC_SUCCESS);
6858 }
6859 
6860 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6861 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)
6862 {
6863   PetscSection    clSection;
6864   IS              clPoints;
6865   PetscScalar    *array;
6866   PetscInt       *points = NULL;
6867   const PetscInt *clp;
6868   PetscInt        numFields, numPoints, p;
6869   PetscInt        offset = 0, f;
6870 
6871   PetscFunctionBeginHot;
6872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6873   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6874   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6875   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6876   PetscCall(PetscSectionGetNumFields(section, &numFields));
6877   /* Get points */
6878   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6879   /* Get array */
6880   PetscCall(VecGetArray(v, &array));
6881   /* Get values */
6882   for (f = 0; f < numFields; ++f) {
6883     const PetscInt    **perms = NULL;
6884     const PetscScalar **flips = NULL;
6885     PetscBool           contains;
6886 
6887     if (!fieldActive[f]) {
6888       for (p = 0; p < numPoints * 2; p += 2) {
6889         PetscInt fdof;
6890         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6891         offset += fdof;
6892       }
6893       continue;
6894     }
6895     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6896     switch (mode) {
6897     case INSERT_VALUES:
6898       for (p = 0; p < numPoints; p++) {
6899         const PetscInt     point = points[2 * p];
6900         const PetscInt    *perm  = perms ? perms[p] : NULL;
6901         const PetscScalar *flip  = flips ? flips[p] : NULL;
6902         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6903         if (!contains) continue;
6904         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6905       }
6906       break;
6907     case INSERT_ALL_VALUES:
6908       for (p = 0; p < numPoints; p++) {
6909         const PetscInt     point = points[2 * p];
6910         const PetscInt    *perm  = perms ? perms[p] : NULL;
6911         const PetscScalar *flip  = flips ? flips[p] : NULL;
6912         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6913         if (!contains) continue;
6914         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6915       }
6916       break;
6917     case INSERT_BC_VALUES:
6918       for (p = 0; p < numPoints; p++) {
6919         const PetscInt     point = points[2 * p];
6920         const PetscInt    *perm  = perms ? perms[p] : NULL;
6921         const PetscScalar *flip  = flips ? flips[p] : NULL;
6922         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6923         if (!contains) continue;
6924         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6925       }
6926       break;
6927     case ADD_VALUES:
6928       for (p = 0; p < numPoints; p++) {
6929         const PetscInt     point = points[2 * p];
6930         const PetscInt    *perm  = perms ? perms[p] : NULL;
6931         const PetscScalar *flip  = flips ? flips[p] : NULL;
6932         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6933         if (!contains) continue;
6934         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6935       }
6936       break;
6937     case ADD_ALL_VALUES:
6938       for (p = 0; p < numPoints; p++) {
6939         const PetscInt     point = points[2 * p];
6940         const PetscInt    *perm  = perms ? perms[p] : NULL;
6941         const PetscScalar *flip  = flips ? flips[p] : NULL;
6942         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6943         if (!contains) continue;
6944         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6945       }
6946       break;
6947     default:
6948       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6949     }
6950     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6951   }
6952   /* Cleanup points */
6953   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6954   /* Cleanup array */
6955   PetscCall(VecRestoreArray(v, &array));
6956   PetscFunctionReturn(PETSC_SUCCESS);
6957 }
6958 
6959 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6960 {
6961   PetscMPIInt rank;
6962   PetscInt    i, j;
6963 
6964   PetscFunctionBegin;
6965   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6966   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6967   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6968   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6969   numCIndices = numCIndices ? numCIndices : numRIndices;
6970   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6971   for (i = 0; i < numRIndices; i++) {
6972     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6973     for (j = 0; j < numCIndices; j++) {
6974 #if defined(PETSC_USE_COMPLEX)
6975       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6976 #else
6977       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6978 #endif
6979     }
6980     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6981   }
6982   PetscFunctionReturn(PETSC_SUCCESS);
6983 }
6984 
6985 /*
6986   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6987 
6988   Input Parameters:
6989 + section - The section for this data layout
6990 . islocal - Is the section (and thus indices being requested) local or global?
6991 . point   - The point contributing dofs with these indices
6992 . off     - The global offset of this point
6993 . loff    - The local offset of each field
6994 . setBC   - The flag determining whether to include indices of boundary values
6995 . perm    - A permutation of the dofs on this point, or NULL
6996 - indperm - A permutation of the entire indices array, or NULL
6997 
6998   Output Parameter:
6999 . indices - Indices for dofs on this point
7000 
7001   Level: developer
7002 
7003   Note: The indices could be local or global, depending on the value of 'off'.
7004 */
7005 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7006 {
7007   PetscInt        dof;   /* The number of unknowns on this point */
7008   PetscInt        cdof;  /* The number of constraints on this point */
7009   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7010   PetscInt        cind = 0, k;
7011 
7012   PetscFunctionBegin;
7013   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7014   PetscCall(PetscSectionGetDof(section, point, &dof));
7015   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7016   if (!cdof || setBC) {
7017     for (k = 0; k < dof; ++k) {
7018       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7019       const PetscInt ind    = indperm ? indperm[preind] : preind;
7020 
7021       indices[ind] = off + k;
7022     }
7023   } else {
7024     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7025     for (k = 0; k < dof; ++k) {
7026       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7027       const PetscInt ind    = indperm ? indperm[preind] : preind;
7028 
7029       if ((cind < cdof) && (k == cdofs[cind])) {
7030         /* Insert check for returning constrained indices */
7031         indices[ind] = -(off + k + 1);
7032         ++cind;
7033       } else {
7034         indices[ind] = off + k - (islocal ? 0 : cind);
7035       }
7036     }
7037   }
7038   *loff += dof;
7039   PetscFunctionReturn(PETSC_SUCCESS);
7040 }
7041 
7042 /*
7043  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7044 
7045  Input Parameters:
7046 + section - a section (global or local)
7047 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7048 . point - point within section
7049 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7050 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7051 . setBC - identify constrained (boundary condition) points via involution.
7052 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7053 . permsoff - offset
7054 - indperm - index permutation
7055 
7056  Output Parameter:
7057 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7058 . indices - array to hold indices (as defined by section) of each dof associated with point
7059 
7060  Notes:
7061  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7062  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7063  in the local vector.
7064 
7065  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7066  significant).  It is invalid to call with a global section and setBC=true.
7067 
7068  Developer Note:
7069  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7070  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7071  offset could be obtained from the section instead of passing it explicitly as we do now.
7072 
7073  Example:
7074  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7075  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7076  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7077  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.
7078 
7079  Level: developer
7080 */
7081 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[])
7082 {
7083   PetscInt numFields, foff, f;
7084 
7085   PetscFunctionBegin;
7086   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7087   PetscCall(PetscSectionGetNumFields(section, &numFields));
7088   for (f = 0, foff = 0; f < numFields; ++f) {
7089     PetscInt        fdof, cfdof;
7090     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7091     PetscInt        cind = 0, b;
7092     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7093 
7094     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7095     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7096     if (!cfdof || setBC) {
7097       for (b = 0; b < fdof; ++b) {
7098         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7099         const PetscInt ind    = indperm ? indperm[preind] : preind;
7100 
7101         indices[ind] = off + foff + b;
7102       }
7103     } else {
7104       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7105       for (b = 0; b < fdof; ++b) {
7106         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7107         const PetscInt ind    = indperm ? indperm[preind] : preind;
7108 
7109         if ((cind < cfdof) && (b == fcdofs[cind])) {
7110           indices[ind] = -(off + foff + b + 1);
7111           ++cind;
7112         } else {
7113           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7114         }
7115       }
7116     }
7117     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7118     foffs[f] += fdof;
7119   }
7120   PetscFunctionReturn(PETSC_SUCCESS);
7121 }
7122 
7123 /*
7124   This version believes the globalSection offsets for each field, rather than just the point offset
7125 
7126  . foffs - The offset into 'indices' for each field, since it is segregated by field
7127 
7128  Notes:
7129  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7130  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7131 */
7132 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7133 {
7134   PetscInt numFields, foff, f;
7135 
7136   PetscFunctionBegin;
7137   PetscCall(PetscSectionGetNumFields(section, &numFields));
7138   for (f = 0; f < numFields; ++f) {
7139     PetscInt        fdof, cfdof;
7140     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7141     PetscInt        cind = 0, b;
7142     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7143 
7144     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7145     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7146     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7147     if (!cfdof) {
7148       for (b = 0; b < fdof; ++b) {
7149         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7150         const PetscInt ind    = indperm ? indperm[preind] : preind;
7151 
7152         indices[ind] = foff + b;
7153       }
7154     } else {
7155       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7156       for (b = 0; b < fdof; ++b) {
7157         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7158         const PetscInt ind    = indperm ? indperm[preind] : preind;
7159 
7160         if ((cind < cfdof) && (b == fcdofs[cind])) {
7161           indices[ind] = -(foff + b + 1);
7162           ++cind;
7163         } else {
7164           indices[ind] = foff + b - cind;
7165         }
7166       }
7167     }
7168     foffs[f] += fdof;
7169   }
7170   PetscFunctionReturn(PETSC_SUCCESS);
7171 }
7172 
7173 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)
7174 {
7175   Mat             cMat;
7176   PetscSection    aSec, cSec;
7177   IS              aIS;
7178   PetscInt        aStart = -1, aEnd = -1;
7179   const PetscInt *anchors;
7180   PetscInt        numFields, f, p, q, newP = 0;
7181   PetscInt        newNumPoints = 0, newNumIndices = 0;
7182   PetscInt       *newPoints, *indices, *newIndices;
7183   PetscInt        maxAnchor, maxDof;
7184   PetscInt        newOffsets[32];
7185   PetscInt       *pointMatOffsets[32];
7186   PetscInt       *newPointOffsets[32];
7187   PetscScalar    *pointMat[32];
7188   PetscScalar    *newValues      = NULL, *tmpValues;
7189   PetscBool       anyConstrained = PETSC_FALSE;
7190 
7191   PetscFunctionBegin;
7192   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7193   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7194   PetscCall(PetscSectionGetNumFields(section, &numFields));
7195 
7196   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7197   /* if there are point-to-point constraints */
7198   if (aSec) {
7199     PetscCall(PetscArrayzero(newOffsets, 32));
7200     PetscCall(ISGetIndices(aIS, &anchors));
7201     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7202     /* figure out how many points are going to be in the new element matrix
7203      * (we allow double counting, because it's all just going to be summed
7204      * into the global matrix anyway) */
7205     for (p = 0; p < 2 * numPoints; p += 2) {
7206       PetscInt b    = points[p];
7207       PetscInt bDof = 0, bSecDof;
7208 
7209       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7210       if (!bSecDof) continue;
7211       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7212       if (bDof) {
7213         /* this point is constrained */
7214         /* it is going to be replaced by its anchors */
7215         PetscInt bOff, q;
7216 
7217         anyConstrained = PETSC_TRUE;
7218         newNumPoints += bDof;
7219         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7220         for (q = 0; q < bDof; q++) {
7221           PetscInt a = anchors[bOff + q];
7222           PetscInt aDof;
7223 
7224           PetscCall(PetscSectionGetDof(section, a, &aDof));
7225           newNumIndices += aDof;
7226           for (f = 0; f < numFields; ++f) {
7227             PetscInt fDof;
7228 
7229             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7230             newOffsets[f + 1] += fDof;
7231           }
7232         }
7233       } else {
7234         /* this point is not constrained */
7235         newNumPoints++;
7236         newNumIndices += bSecDof;
7237         for (f = 0; f < numFields; ++f) {
7238           PetscInt fDof;
7239 
7240           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7241           newOffsets[f + 1] += fDof;
7242         }
7243       }
7244     }
7245   }
7246   if (!anyConstrained) {
7247     if (outNumPoints) *outNumPoints = 0;
7248     if (outNumIndices) *outNumIndices = 0;
7249     if (outPoints) *outPoints = NULL;
7250     if (outValues) *outValues = NULL;
7251     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7252     PetscFunctionReturn(PETSC_SUCCESS);
7253   }
7254 
7255   if (outNumPoints) *outNumPoints = newNumPoints;
7256   if (outNumIndices) *outNumIndices = newNumIndices;
7257 
7258   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7259 
7260   if (!outPoints && !outValues) {
7261     if (offsets) {
7262       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7263     }
7264     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7265     PetscFunctionReturn(PETSC_SUCCESS);
7266   }
7267 
7268   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7269 
7270   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7271 
7272   /* workspaces */
7273   if (numFields) {
7274     for (f = 0; f < numFields; f++) {
7275       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7276       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7277     }
7278   } else {
7279     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7280     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7281   }
7282 
7283   /* get workspaces for the point-to-point matrices */
7284   if (numFields) {
7285     PetscInt totalOffset, totalMatOffset;
7286 
7287     for (p = 0; p < numPoints; p++) {
7288       PetscInt b    = points[2 * p];
7289       PetscInt bDof = 0, bSecDof;
7290 
7291       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7292       if (!bSecDof) {
7293         for (f = 0; f < numFields; f++) {
7294           newPointOffsets[f][p + 1] = 0;
7295           pointMatOffsets[f][p + 1] = 0;
7296         }
7297         continue;
7298       }
7299       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7300       if (bDof) {
7301         for (f = 0; f < numFields; f++) {
7302           PetscInt fDof, q, bOff, allFDof = 0;
7303 
7304           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7305           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7306           for (q = 0; q < bDof; q++) {
7307             PetscInt a = anchors[bOff + q];
7308             PetscInt aFDof;
7309 
7310             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7311             allFDof += aFDof;
7312           }
7313           newPointOffsets[f][p + 1] = allFDof;
7314           pointMatOffsets[f][p + 1] = fDof * allFDof;
7315         }
7316       } else {
7317         for (f = 0; f < numFields; f++) {
7318           PetscInt fDof;
7319 
7320           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7321           newPointOffsets[f][p + 1] = fDof;
7322           pointMatOffsets[f][p + 1] = 0;
7323         }
7324       }
7325     }
7326     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7327       newPointOffsets[f][0] = totalOffset;
7328       pointMatOffsets[f][0] = totalMatOffset;
7329       for (p = 0; p < numPoints; p++) {
7330         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7331         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7332       }
7333       totalOffset    = newPointOffsets[f][numPoints];
7334       totalMatOffset = pointMatOffsets[f][numPoints];
7335       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7336     }
7337   } else {
7338     for (p = 0; p < numPoints; p++) {
7339       PetscInt b    = points[2 * p];
7340       PetscInt bDof = 0, bSecDof;
7341 
7342       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7343       if (!bSecDof) {
7344         newPointOffsets[0][p + 1] = 0;
7345         pointMatOffsets[0][p + 1] = 0;
7346         continue;
7347       }
7348       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7349       if (bDof) {
7350         PetscInt bOff, q, allDof = 0;
7351 
7352         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7353         for (q = 0; q < bDof; q++) {
7354           PetscInt a = anchors[bOff + q], aDof;
7355 
7356           PetscCall(PetscSectionGetDof(section, a, &aDof));
7357           allDof += aDof;
7358         }
7359         newPointOffsets[0][p + 1] = allDof;
7360         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7361       } else {
7362         newPointOffsets[0][p + 1] = bSecDof;
7363         pointMatOffsets[0][p + 1] = 0;
7364       }
7365     }
7366     newPointOffsets[0][0] = 0;
7367     pointMatOffsets[0][0] = 0;
7368     for (p = 0; p < numPoints; p++) {
7369       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7370       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7371     }
7372     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7373   }
7374 
7375   /* output arrays */
7376   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7377 
7378   /* get the point-to-point matrices; construct newPoints */
7379   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7380   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7381   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7382   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7383   if (numFields) {
7384     for (p = 0, newP = 0; p < numPoints; p++) {
7385       PetscInt b    = points[2 * p];
7386       PetscInt o    = points[2 * p + 1];
7387       PetscInt bDof = 0, bSecDof;
7388 
7389       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7390       if (!bSecDof) continue;
7391       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7392       if (bDof) {
7393         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7394 
7395         fStart[0] = 0;
7396         fEnd[0]   = 0;
7397         for (f = 0; f < numFields; f++) {
7398           PetscInt fDof;
7399 
7400           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7401           fStart[f + 1] = fStart[f] + fDof;
7402           fEnd[f + 1]   = fStart[f + 1];
7403         }
7404         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7405         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7406 
7407         fAnchorStart[0] = 0;
7408         fAnchorEnd[0]   = 0;
7409         for (f = 0; f < numFields; f++) {
7410           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7411 
7412           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7413           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7414         }
7415         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7416         for (q = 0; q < bDof; q++) {
7417           PetscInt a = anchors[bOff + q], aOff;
7418 
7419           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7420           newPoints[2 * (newP + q)]     = a;
7421           newPoints[2 * (newP + q) + 1] = 0;
7422           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7423           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7424         }
7425         newP += bDof;
7426 
7427         if (outValues) {
7428           /* get the point-to-point submatrix */
7429           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]));
7430         }
7431       } else {
7432         newPoints[2 * newP]     = b;
7433         newPoints[2 * newP + 1] = o;
7434         newP++;
7435       }
7436     }
7437   } else {
7438     for (p = 0; p < numPoints; p++) {
7439       PetscInt b    = points[2 * p];
7440       PetscInt o    = points[2 * p + 1];
7441       PetscInt bDof = 0, bSecDof;
7442 
7443       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7444       if (!bSecDof) continue;
7445       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7446       if (bDof) {
7447         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7448 
7449         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7450         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7451 
7452         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7453         for (q = 0; q < bDof; q++) {
7454           PetscInt a = anchors[bOff + q], aOff;
7455 
7456           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7457 
7458           newPoints[2 * (newP + q)]     = a;
7459           newPoints[2 * (newP + q) + 1] = 0;
7460           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7461           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7462         }
7463         newP += bDof;
7464 
7465         /* get the point-to-point submatrix */
7466         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7467       } else {
7468         newPoints[2 * newP]     = b;
7469         newPoints[2 * newP + 1] = o;
7470         newP++;
7471       }
7472     }
7473   }
7474 
7475   if (outValues) {
7476     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7477     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7478     /* multiply constraints on the right */
7479     if (numFields) {
7480       for (f = 0; f < numFields; f++) {
7481         PetscInt oldOff = offsets[f];
7482 
7483         for (p = 0; p < numPoints; p++) {
7484           PetscInt cStart = newPointOffsets[f][p];
7485           PetscInt b      = points[2 * p];
7486           PetscInt c, r, k;
7487           PetscInt dof;
7488 
7489           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7490           if (!dof) continue;
7491           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7492             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7493             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7494 
7495             for (r = 0; r < numIndices; r++) {
7496               for (c = 0; c < nCols; c++) {
7497                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7498               }
7499             }
7500           } else {
7501             /* copy this column as is */
7502             for (r = 0; r < numIndices; r++) {
7503               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7504             }
7505           }
7506           oldOff += dof;
7507         }
7508       }
7509     } else {
7510       PetscInt oldOff = 0;
7511       for (p = 0; p < numPoints; p++) {
7512         PetscInt cStart = newPointOffsets[0][p];
7513         PetscInt b      = points[2 * p];
7514         PetscInt c, r, k;
7515         PetscInt dof;
7516 
7517         PetscCall(PetscSectionGetDof(section, b, &dof));
7518         if (!dof) continue;
7519         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7520           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7521           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7522 
7523           for (r = 0; r < numIndices; r++) {
7524             for (c = 0; c < nCols; c++) {
7525               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7526             }
7527           }
7528         } else {
7529           /* copy this column as is */
7530           for (r = 0; r < numIndices; r++) {
7531             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7532           }
7533         }
7534         oldOff += dof;
7535       }
7536     }
7537 
7538     if (multiplyLeft) {
7539       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7540       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7541       /* multiply constraints transpose on the left */
7542       if (numFields) {
7543         for (f = 0; f < numFields; f++) {
7544           PetscInt oldOff = offsets[f];
7545 
7546           for (p = 0; p < numPoints; p++) {
7547             PetscInt rStart = newPointOffsets[f][p];
7548             PetscInt b      = points[2 * p];
7549             PetscInt c, r, k;
7550             PetscInt dof;
7551 
7552             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7553             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7554               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7555               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7556 
7557               for (r = 0; r < nRows; r++) {
7558                 for (c = 0; c < newNumIndices; c++) {
7559                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7560                 }
7561               }
7562             } else {
7563               /* copy this row as is */
7564               for (r = 0; r < dof; r++) {
7565                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7566               }
7567             }
7568             oldOff += dof;
7569           }
7570         }
7571       } else {
7572         PetscInt oldOff = 0;
7573 
7574         for (p = 0; p < numPoints; p++) {
7575           PetscInt rStart = newPointOffsets[0][p];
7576           PetscInt b      = points[2 * p];
7577           PetscInt c, r, k;
7578           PetscInt dof;
7579 
7580           PetscCall(PetscSectionGetDof(section, b, &dof));
7581           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7582             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7583             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7584 
7585             for (r = 0; r < nRows; r++) {
7586               for (c = 0; c < newNumIndices; c++) {
7587                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7588               }
7589             }
7590           } else {
7591             /* copy this row as is */
7592             for (r = 0; r < dof; r++) {
7593               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7594             }
7595           }
7596           oldOff += dof;
7597         }
7598       }
7599 
7600       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7601     } else {
7602       newValues = tmpValues;
7603     }
7604   }
7605 
7606   /* clean up */
7607   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7608   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7609 
7610   if (numFields) {
7611     for (f = 0; f < numFields; f++) {
7612       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7613       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7614       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7615     }
7616   } else {
7617     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7618     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7619     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7620   }
7621   PetscCall(ISRestoreIndices(aIS, &anchors));
7622 
7623   /* output */
7624   if (outPoints) {
7625     *outPoints = newPoints;
7626   } else {
7627     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7628   }
7629   if (outValues) *outValues = newValues;
7630   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7631   PetscFunctionReturn(PETSC_SUCCESS);
7632 }
7633 
7634 /*@C
7635   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7636 
7637   Not collective
7638 
7639   Input Parameters:
7640 + dm         - The `DM`
7641 . section    - The `PetscSection` describing the points (a local section)
7642 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7643 . point      - The point defining the closure
7644 - useClPerm  - Use the closure point permutation if available
7645 
7646   Output Parameters:
7647 + numIndices - The number of dof indices in the closure of point with the input sections
7648 . indices    - The dof indices
7649 . outOffsets - Array to write the field offsets into, or `NULL`
7650 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7651 
7652   Level: advanced
7653 
7654   Notes:
7655   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7656 
7657   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7658   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7659   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7660   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7661   indices (with the above semantics) are implied.
7662 
7663 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7664           `PetscSection`, `DMGetGlobalSection()`
7665 @*/
7666 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7667 {
7668   /* Closure ordering */
7669   PetscSection    clSection;
7670   IS              clPoints;
7671   const PetscInt *clp;
7672   PetscInt       *points;
7673   const PetscInt *clperm = NULL;
7674   /* Dof permutation and sign flips */
7675   const PetscInt    **perms[32] = {NULL};
7676   const PetscScalar **flips[32] = {NULL};
7677   PetscScalar        *valCopy   = NULL;
7678   /* Hanging node constraints */
7679   PetscInt    *pointsC = NULL;
7680   PetscScalar *valuesC = NULL;
7681   PetscInt     NclC, NiC;
7682 
7683   PetscInt *idx;
7684   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7685   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7686 
7687   PetscFunctionBeginHot;
7688   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7689   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7690   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7691   if (numIndices) PetscAssertPointer(numIndices, 6);
7692   if (indices) PetscAssertPointer(indices, 7);
7693   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7694   if (values) PetscAssertPointer(values, 9);
7695   PetscCall(PetscSectionGetNumFields(section, &Nf));
7696   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7697   PetscCall(PetscArrayzero(offsets, 32));
7698   /* 1) Get points in closure */
7699   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7700   if (useClPerm) {
7701     PetscInt depth, clsize;
7702     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7703     for (clsize = 0, p = 0; p < Ncl; p++) {
7704       PetscInt dof;
7705       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7706       clsize += dof;
7707     }
7708     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7709   }
7710   /* 2) Get number of indices on these points and field offsets from section */
7711   for (p = 0; p < Ncl * 2; p += 2) {
7712     PetscInt dof, fdof;
7713 
7714     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7715     for (f = 0; f < Nf; ++f) {
7716       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7717       offsets[f + 1] += fdof;
7718     }
7719     Ni += dof;
7720   }
7721   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7722   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7723   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7724   for (f = 0; f < PetscMax(1, Nf); ++f) {
7725     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7726     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7727     /* may need to apply sign changes to the element matrix */
7728     if (values && flips[f]) {
7729       PetscInt foffset = offsets[f];
7730 
7731       for (p = 0; p < Ncl; ++p) {
7732         PetscInt           pnt  = points[2 * p], fdof;
7733         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7734 
7735         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7736         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7737         if (flip) {
7738           PetscInt i, j, k;
7739 
7740           if (!valCopy) {
7741             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7742             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7743             *values = valCopy;
7744           }
7745           for (i = 0; i < fdof; ++i) {
7746             PetscScalar fval = flip[i];
7747 
7748             for (k = 0; k < Ni; ++k) {
7749               valCopy[Ni * (foffset + i) + k] *= fval;
7750               valCopy[Ni * k + (foffset + i)] *= fval;
7751             }
7752           }
7753         }
7754         foffset += fdof;
7755       }
7756     }
7757   }
7758   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7759   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7760   if (NclC) {
7761     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7762     for (f = 0; f < PetscMax(1, Nf); ++f) {
7763       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7764       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7765     }
7766     for (f = 0; f < PetscMax(1, Nf); ++f) {
7767       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7768       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7769     }
7770     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7771     Ncl    = NclC;
7772     Ni     = NiC;
7773     points = pointsC;
7774     if (values) *values = valuesC;
7775   }
7776   /* 5) Calculate indices */
7777   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7778   if (Nf) {
7779     PetscInt  idxOff;
7780     PetscBool useFieldOffsets;
7781 
7782     if (outOffsets) {
7783       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7784     }
7785     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7786     if (useFieldOffsets) {
7787       for (p = 0; p < Ncl; ++p) {
7788         const PetscInt pnt = points[p * 2];
7789 
7790         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7791       }
7792     } else {
7793       for (p = 0; p < Ncl; ++p) {
7794         const PetscInt pnt = points[p * 2];
7795 
7796         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7797         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7798          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7799          * global section. */
7800         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7801       }
7802     }
7803   } else {
7804     PetscInt off = 0, idxOff;
7805 
7806     for (p = 0; p < Ncl; ++p) {
7807       const PetscInt  pnt  = points[p * 2];
7808       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7809 
7810       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7811       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7812        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7813       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7814     }
7815   }
7816   /* 6) Cleanup */
7817   for (f = 0; f < PetscMax(1, Nf); ++f) {
7818     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7819     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7820   }
7821   if (NclC) {
7822     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7823   } else {
7824     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7825   }
7826 
7827   if (numIndices) *numIndices = Ni;
7828   if (indices) *indices = idx;
7829   PetscFunctionReturn(PETSC_SUCCESS);
7830 }
7831 
7832 /*@C
7833   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7834 
7835   Not collective
7836 
7837   Input Parameters:
7838 + dm         - The `DM`
7839 . section    - The `PetscSection` describing the points (a local section)
7840 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7841 . point      - The point defining the closure
7842 - useClPerm  - Use the closure point permutation if available
7843 
7844   Output Parameters:
7845 + numIndices - The number of dof indices in the closure of point with the input sections
7846 . indices    - The dof indices
7847 . outOffsets - Array to write the field offsets into, or `NULL`
7848 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7849 
7850   Level: advanced
7851 
7852   Notes:
7853   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7854 
7855   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7856   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7857   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7858   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7859   indices (with the above semantics) are implied.
7860 
7861 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7862 @*/
7863 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7864 {
7865   PetscFunctionBegin;
7866   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7867   PetscAssertPointer(indices, 7);
7868   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7869   PetscFunctionReturn(PETSC_SUCCESS);
7870 }
7871 
7872 /*@C
7873   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7874 
7875   Not collective
7876 
7877   Input Parameters:
7878 + dm            - The `DM`
7879 . section       - The section describing the layout in `v`, or `NULL` to use the default section
7880 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7881 . A             - The matrix
7882 . point         - The point in the `DM`
7883 . values        - The array of values
7884 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7885 
7886   Level: intermediate
7887 
7888 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7889 @*/
7890 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7891 {
7892   DM_Plex           *mesh = (DM_Plex *)dm->data;
7893   PetscInt          *indices;
7894   PetscInt           numIndices;
7895   const PetscScalar *valuesOrig = values;
7896   PetscErrorCode     ierr;
7897 
7898   PetscFunctionBegin;
7899   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7900   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7901   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7902   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7903   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7904   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7905 
7906   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7907 
7908   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7909   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7910   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7911   if (ierr) {
7912     PetscMPIInt rank;
7913 
7914     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7915     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7916     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7917     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7918     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7919     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7920   }
7921   if (mesh->printFEM > 1) {
7922     PetscInt i;
7923     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7924     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7925     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7926   }
7927 
7928   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7929   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7930   PetscFunctionReturn(PETSC_SUCCESS);
7931 }
7932 
7933 /*@C
7934   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
7935 
7936   Not collective
7937 
7938   Input Parameters:
7939 + dmRow            - The `DM` for the row fields
7940 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
7941 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7942 . dmCol            - The `DM` for the column fields
7943 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
7944 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7945 . A                - The matrix
7946 . point            - The point in the `DM`
7947 . values           - The array of values
7948 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7949 
7950   Level: intermediate
7951 
7952 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7953 @*/
7954 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7955 {
7956   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7957   PetscInt          *indicesRow, *indicesCol;
7958   PetscInt           numIndicesRow, numIndicesCol;
7959   const PetscScalar *valuesOrig = values;
7960   PetscErrorCode     ierr;
7961 
7962   PetscFunctionBegin;
7963   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7964   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7965   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7966   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7967   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7968   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7969   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7970   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7971   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7972   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7973   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7974 
7975   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7976   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7977 
7978   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7979   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7980   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7981   if (ierr) {
7982     PetscMPIInt rank;
7983 
7984     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7985     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7986     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7987     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7988     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7989     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7990   }
7991 
7992   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7993   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7994   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7995   PetscFunctionReturn(PETSC_SUCCESS);
7996 }
7997 
7998 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7999 {
8000   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8001   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8002   PetscInt       *cpoints = NULL;
8003   PetscInt       *findices, *cindices;
8004   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8005   PetscInt        foffsets[32], coffsets[32];
8006   DMPolytopeType  ct;
8007   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8008   PetscErrorCode  ierr;
8009 
8010   PetscFunctionBegin;
8011   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8012   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8013   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8014   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8015   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8016   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8017   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8018   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8019   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8020   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8021   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8022   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8023   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8024   PetscCall(PetscArrayzero(foffsets, 32));
8025   PetscCall(PetscArrayzero(coffsets, 32));
8026   /* Column indices */
8027   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8028   maxFPoints = numCPoints;
8029   /* Compress out points not in the section */
8030   /*   TODO: Squeeze out points with 0 dof as well */
8031   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8032   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8033     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8034       cpoints[q * 2]     = cpoints[p];
8035       cpoints[q * 2 + 1] = cpoints[p + 1];
8036       ++q;
8037     }
8038   }
8039   numCPoints = q;
8040   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8041     PetscInt fdof;
8042 
8043     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8044     if (!dof) continue;
8045     for (f = 0; f < numFields; ++f) {
8046       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8047       coffsets[f + 1] += fdof;
8048     }
8049     numCIndices += dof;
8050   }
8051   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8052   /* Row indices */
8053   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8054   {
8055     DMPlexTransform tr;
8056     DMPolytopeType *rct;
8057     PetscInt       *rsize, *rcone, *rornt, Nt;
8058 
8059     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8060     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8061     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8062     numSubcells = rsize[Nt - 1];
8063     PetscCall(DMPlexTransformDestroy(&tr));
8064   }
8065   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8066   for (r = 0, q = 0; r < numSubcells; ++r) {
8067     /* TODO Map from coarse to fine cells */
8068     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8069     /* Compress out points not in the section */
8070     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8071     for (p = 0; p < numFPoints * 2; p += 2) {
8072       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8073         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8074         if (!dof) continue;
8075         for (s = 0; s < q; ++s)
8076           if (fpoints[p] == ftotpoints[s * 2]) break;
8077         if (s < q) continue;
8078         ftotpoints[q * 2]     = fpoints[p];
8079         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8080         ++q;
8081       }
8082     }
8083     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8084   }
8085   numFPoints = q;
8086   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8087     PetscInt fdof;
8088 
8089     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8090     if (!dof) continue;
8091     for (f = 0; f < numFields; ++f) {
8092       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8093       foffsets[f + 1] += fdof;
8094     }
8095     numFIndices += dof;
8096   }
8097   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8098 
8099   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8100   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8101   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8102   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8103   if (numFields) {
8104     const PetscInt **permsF[32] = {NULL};
8105     const PetscInt **permsC[32] = {NULL};
8106 
8107     for (f = 0; f < numFields; f++) {
8108       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8109       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8110     }
8111     for (p = 0; p < numFPoints; p++) {
8112       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8113       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8114     }
8115     for (p = 0; p < numCPoints; p++) {
8116       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8117       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8118     }
8119     for (f = 0; f < numFields; f++) {
8120       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8121       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8122     }
8123   } else {
8124     const PetscInt **permsF = NULL;
8125     const PetscInt **permsC = NULL;
8126 
8127     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8128     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8129     for (p = 0, off = 0; p < numFPoints; p++) {
8130       const PetscInt *perm = permsF ? permsF[p] : NULL;
8131 
8132       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8133       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8134     }
8135     for (p = 0, off = 0; p < numCPoints; p++) {
8136       const PetscInt *perm = permsC ? permsC[p] : NULL;
8137 
8138       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8139       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8140     }
8141     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8142     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8143   }
8144   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8145   /* TODO: flips */
8146   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8147   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8148   if (ierr) {
8149     PetscMPIInt rank;
8150 
8151     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8152     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8153     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8154     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8155     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8156   }
8157   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8158   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8159   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8160   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8161   PetscFunctionReturn(PETSC_SUCCESS);
8162 }
8163 
8164 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8165 {
8166   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8167   PetscInt       *cpoints = NULL;
8168   PetscInt        foffsets[32], coffsets[32];
8169   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8170   DMPolytopeType  ct;
8171   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8172 
8173   PetscFunctionBegin;
8174   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8175   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8176   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8177   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8178   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8179   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8180   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8181   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8182   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8183   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8184   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8185   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8186   PetscCall(PetscArrayzero(foffsets, 32));
8187   PetscCall(PetscArrayzero(coffsets, 32));
8188   /* Column indices */
8189   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8190   maxFPoints = numCPoints;
8191   /* Compress out points not in the section */
8192   /*   TODO: Squeeze out points with 0 dof as well */
8193   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8194   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8195     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8196       cpoints[q * 2]     = cpoints[p];
8197       cpoints[q * 2 + 1] = cpoints[p + 1];
8198       ++q;
8199     }
8200   }
8201   numCPoints = q;
8202   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8203     PetscInt fdof;
8204 
8205     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8206     if (!dof) continue;
8207     for (f = 0; f < numFields; ++f) {
8208       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8209       coffsets[f + 1] += fdof;
8210     }
8211     numCIndices += dof;
8212   }
8213   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8214   /* Row indices */
8215   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8216   {
8217     DMPlexTransform tr;
8218     DMPolytopeType *rct;
8219     PetscInt       *rsize, *rcone, *rornt, Nt;
8220 
8221     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8222     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8223     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8224     numSubcells = rsize[Nt - 1];
8225     PetscCall(DMPlexTransformDestroy(&tr));
8226   }
8227   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8228   for (r = 0, q = 0; r < numSubcells; ++r) {
8229     /* TODO Map from coarse to fine cells */
8230     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8231     /* Compress out points not in the section */
8232     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8233     for (p = 0; p < numFPoints * 2; p += 2) {
8234       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8235         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8236         if (!dof) continue;
8237         for (s = 0; s < q; ++s)
8238           if (fpoints[p] == ftotpoints[s * 2]) break;
8239         if (s < q) continue;
8240         ftotpoints[q * 2]     = fpoints[p];
8241         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8242         ++q;
8243       }
8244     }
8245     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8246   }
8247   numFPoints = q;
8248   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8249     PetscInt fdof;
8250 
8251     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8252     if (!dof) continue;
8253     for (f = 0; f < numFields; ++f) {
8254       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8255       foffsets[f + 1] += fdof;
8256     }
8257     numFIndices += dof;
8258   }
8259   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8260 
8261   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8262   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8263   if (numFields) {
8264     const PetscInt **permsF[32] = {NULL};
8265     const PetscInt **permsC[32] = {NULL};
8266 
8267     for (f = 0; f < numFields; f++) {
8268       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8269       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8270     }
8271     for (p = 0; p < numFPoints; p++) {
8272       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8273       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8274     }
8275     for (p = 0; p < numCPoints; p++) {
8276       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8277       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8278     }
8279     for (f = 0; f < numFields; f++) {
8280       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8281       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8282     }
8283   } else {
8284     const PetscInt **permsF = NULL;
8285     const PetscInt **permsC = NULL;
8286 
8287     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8288     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8289     for (p = 0, off = 0; p < numFPoints; p++) {
8290       const PetscInt *perm = permsF ? permsF[p] : NULL;
8291 
8292       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8293       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8294     }
8295     for (p = 0, off = 0; p < numCPoints; p++) {
8296       const PetscInt *perm = permsC ? permsC[p] : NULL;
8297 
8298       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8299       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8300     }
8301     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8302     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8303   }
8304   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8305   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8306   PetscFunctionReturn(PETSC_SUCCESS);
8307 }
8308 
8309 /*@C
8310   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8311 
8312   Input Parameter:
8313 . dm - The `DMPLEX` object
8314 
8315   Output Parameter:
8316 . cellHeight - The height of a cell
8317 
8318   Level: developer
8319 
8320 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8321 @*/
8322 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8323 {
8324   DM_Plex *mesh = (DM_Plex *)dm->data;
8325 
8326   PetscFunctionBegin;
8327   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8328   PetscAssertPointer(cellHeight, 2);
8329   *cellHeight = mesh->vtkCellHeight;
8330   PetscFunctionReturn(PETSC_SUCCESS);
8331 }
8332 
8333 /*@C
8334   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8335 
8336   Input Parameters:
8337 + dm         - The `DMPLEX` object
8338 - cellHeight - The height of a cell
8339 
8340   Level: developer
8341 
8342 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8343 @*/
8344 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8345 {
8346   DM_Plex *mesh = (DM_Plex *)dm->data;
8347 
8348   PetscFunctionBegin;
8349   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8350   mesh->vtkCellHeight = cellHeight;
8351   PetscFunctionReturn(PETSC_SUCCESS);
8352 }
8353 
8354 /*@
8355   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8356 
8357   Input Parameters:
8358 + dm - The `DMPLEX` object
8359 - ct - The `DMPolytopeType` of the cell
8360 
8361   Output Parameters:
8362 + start - The first cell of this type, or `NULL`
8363 - end   - The upper bound on this celltype, or `NULL`
8364 
8365   Level: advanced
8366 
8367 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8368 @*/
8369 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8370 {
8371   DM_Plex *mesh = (DM_Plex *)dm->data;
8372   DMLabel  label;
8373   PetscInt pStart, pEnd;
8374 
8375   PetscFunctionBegin;
8376   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8377   if (start) {
8378     PetscAssertPointer(start, 3);
8379     *start = 0;
8380   }
8381   if (end) {
8382     PetscAssertPointer(end, 4);
8383     *end = 0;
8384   }
8385   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8386   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8387   if (mesh->tr) {
8388     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8389   } else {
8390     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8391     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8392     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8393   }
8394   PetscFunctionReturn(PETSC_SUCCESS);
8395 }
8396 
8397 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8398 {
8399   PetscSection section, globalSection;
8400   PetscInt    *numbers, p;
8401 
8402   PetscFunctionBegin;
8403   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8404   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8405   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8406   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8407   PetscCall(PetscSectionSetUp(section));
8408   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8409   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8410   for (p = pStart; p < pEnd; ++p) {
8411     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8412     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8413     else numbers[p - pStart] += shift;
8414   }
8415   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8416   if (globalSize) {
8417     PetscLayout layout;
8418     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8419     PetscCall(PetscLayoutGetSize(layout, globalSize));
8420     PetscCall(PetscLayoutDestroy(&layout));
8421   }
8422   PetscCall(PetscSectionDestroy(&section));
8423   PetscCall(PetscSectionDestroy(&globalSection));
8424   PetscFunctionReturn(PETSC_SUCCESS);
8425 }
8426 
8427 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8428 {
8429   PetscInt cellHeight, cStart, cEnd;
8430 
8431   PetscFunctionBegin;
8432   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8433   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8434   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8435   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8436   PetscFunctionReturn(PETSC_SUCCESS);
8437 }
8438 
8439 /*@
8440   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8441 
8442   Input Parameter:
8443 . dm - The `DMPLEX` object
8444 
8445   Output Parameter:
8446 . globalCellNumbers - Global cell numbers for all cells on this process
8447 
8448   Level: developer
8449 
8450 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8451 @*/
8452 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8453 {
8454   DM_Plex *mesh = (DM_Plex *)dm->data;
8455 
8456   PetscFunctionBegin;
8457   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8458   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8459   *globalCellNumbers = mesh->globalCellNumbers;
8460   PetscFunctionReturn(PETSC_SUCCESS);
8461 }
8462 
8463 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8464 {
8465   PetscInt vStart, vEnd;
8466 
8467   PetscFunctionBegin;
8468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8469   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8470   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8471   PetscFunctionReturn(PETSC_SUCCESS);
8472 }
8473 
8474 /*@
8475   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8476 
8477   Input Parameter:
8478 . dm - The `DMPLEX` object
8479 
8480   Output Parameter:
8481 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8482 
8483   Level: developer
8484 
8485 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8486 @*/
8487 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8488 {
8489   DM_Plex *mesh = (DM_Plex *)dm->data;
8490 
8491   PetscFunctionBegin;
8492   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8493   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8494   *globalVertexNumbers = mesh->globalVertexNumbers;
8495   PetscFunctionReturn(PETSC_SUCCESS);
8496 }
8497 
8498 /*@
8499   DMPlexCreatePointNumbering - Create a global numbering for all points.
8500 
8501   Collective
8502 
8503   Input Parameter:
8504 . dm - The `DMPLEX` object
8505 
8506   Output Parameter:
8507 . globalPointNumbers - Global numbers for all points on this process
8508 
8509   Level: developer
8510 
8511   Notes:
8512   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8513   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8514   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8515   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8516 
8517   The partitioned mesh is
8518   ```
8519   (2)--0--(3)--1--(4)    (1)--0--(2)
8520   ```
8521   and its global numbering is
8522   ```
8523   (3)--0--(4)--1--(5)--2--(6)
8524   ```
8525   Then the global numbering is provided as
8526   ```
8527   [0] Number of indices in set 5
8528   [0] 0 0
8529   [0] 1 1
8530   [0] 2 3
8531   [0] 3 4
8532   [0] 4 -6
8533   [1] Number of indices in set 3
8534   [1] 0 2
8535   [1] 1 5
8536   [1] 2 6
8537   ```
8538 
8539 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8540 @*/
8541 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8542 {
8543   IS        nums[4];
8544   PetscInt  depths[4], gdepths[4], starts[4];
8545   PetscInt  depth, d, shift = 0;
8546   PetscBool empty = PETSC_FALSE;
8547 
8548   PetscFunctionBegin;
8549   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8550   PetscCall(DMPlexGetDepth(dm, &depth));
8551   // For unstratified meshes use dim instead of depth
8552   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8553   // If any stratum is empty, we must mark all empty
8554   for (d = 0; d <= depth; ++d) {
8555     PetscInt end;
8556 
8557     depths[d] = depth - d;
8558     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8559     if (!(starts[d] - end)) empty = PETSC_TRUE;
8560   }
8561   if (empty)
8562     for (d = 0; d <= depth; ++d) {
8563       depths[d] = -1;
8564       starts[d] = -1;
8565     }
8566   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8567   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8568   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]);
8569   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8570   for (d = 0; d <= depth; ++d) {
8571     PetscInt pStart, pEnd, gsize;
8572 
8573     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8574     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8575     shift += gsize;
8576   }
8577   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8578   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8579   PetscFunctionReturn(PETSC_SUCCESS);
8580 }
8581 
8582 /*@
8583   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8584 
8585   Input Parameter:
8586 . dm - The `DMPLEX` object
8587 
8588   Output Parameter:
8589 . ranks - The rank field
8590 
8591   Options Database Key:
8592 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8593 
8594   Level: intermediate
8595 
8596 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8597 @*/
8598 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8599 {
8600   DM             rdm;
8601   PetscFE        fe;
8602   PetscScalar   *r;
8603   PetscMPIInt    rank;
8604   DMPolytopeType ct;
8605   PetscInt       dim, cStart, cEnd, c;
8606   PetscBool      simplex;
8607 
8608   PetscFunctionBeginUser;
8609   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8610   PetscAssertPointer(ranks, 2);
8611   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8612   PetscCall(DMClone(dm, &rdm));
8613   PetscCall(DMGetDimension(rdm, &dim));
8614   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8615   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8616   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8617   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8618   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8619   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8620   PetscCall(PetscFEDestroy(&fe));
8621   PetscCall(DMCreateDS(rdm));
8622   PetscCall(DMCreateGlobalVector(rdm, ranks));
8623   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8624   PetscCall(VecGetArray(*ranks, &r));
8625   for (c = cStart; c < cEnd; ++c) {
8626     PetscScalar *lr;
8627 
8628     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8629     if (lr) *lr = rank;
8630   }
8631   PetscCall(VecRestoreArray(*ranks, &r));
8632   PetscCall(DMDestroy(&rdm));
8633   PetscFunctionReturn(PETSC_SUCCESS);
8634 }
8635 
8636 /*@
8637   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8638 
8639   Input Parameters:
8640 + dm    - The `DMPLEX`
8641 - label - The `DMLabel`
8642 
8643   Output Parameter:
8644 . val - The label value field
8645 
8646   Options Database Key:
8647 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8648 
8649   Level: intermediate
8650 
8651 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8652 @*/
8653 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8654 {
8655   DM           rdm;
8656   PetscFE      fe;
8657   PetscScalar *v;
8658   PetscInt     dim, cStart, cEnd, c;
8659 
8660   PetscFunctionBeginUser;
8661   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8662   PetscAssertPointer(label, 2);
8663   PetscAssertPointer(val, 3);
8664   PetscCall(DMClone(dm, &rdm));
8665   PetscCall(DMGetDimension(rdm, &dim));
8666   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8667   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8668   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8669   PetscCall(PetscFEDestroy(&fe));
8670   PetscCall(DMCreateDS(rdm));
8671   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8672   PetscCall(DMCreateGlobalVector(rdm, val));
8673   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8674   PetscCall(VecGetArray(*val, &v));
8675   for (c = cStart; c < cEnd; ++c) {
8676     PetscScalar *lv;
8677     PetscInt     cval;
8678 
8679     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8680     PetscCall(DMLabelGetValue(label, c, &cval));
8681     *lv = cval;
8682   }
8683   PetscCall(VecRestoreArray(*val, &v));
8684   PetscCall(DMDestroy(&rdm));
8685   PetscFunctionReturn(PETSC_SUCCESS);
8686 }
8687 
8688 /*@
8689   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8690 
8691   Input Parameter:
8692 . dm - The `DMPLEX` object
8693 
8694   Level: developer
8695 
8696   Notes:
8697   This is a useful diagnostic when creating meshes programmatically.
8698 
8699   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8700 
8701 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8702 @*/
8703 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8704 {
8705   PetscSection    coneSection, supportSection;
8706   const PetscInt *cone, *support;
8707   PetscInt        coneSize, c, supportSize, s;
8708   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8709   PetscBool       storagecheck = PETSC_TRUE;
8710 
8711   PetscFunctionBegin;
8712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8713   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8714   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8715   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8716   /* Check that point p is found in the support of its cone points, and vice versa */
8717   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8718   for (p = pStart; p < pEnd; ++p) {
8719     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8720     PetscCall(DMPlexGetCone(dm, p, &cone));
8721     for (c = 0; c < coneSize; ++c) {
8722       PetscBool dup = PETSC_FALSE;
8723       PetscInt  d;
8724       for (d = c - 1; d >= 0; --d) {
8725         if (cone[c] == cone[d]) {
8726           dup = PETSC_TRUE;
8727           break;
8728         }
8729       }
8730       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8731       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8732       for (s = 0; s < supportSize; ++s) {
8733         if (support[s] == p) break;
8734       }
8735       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8736         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8737         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8738         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8739         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8740         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8741         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8742         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]);
8743         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8744       }
8745     }
8746     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8747     if (p != pp) {
8748       storagecheck = PETSC_FALSE;
8749       continue;
8750     }
8751     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8752     PetscCall(DMPlexGetSupport(dm, p, &support));
8753     for (s = 0; s < supportSize; ++s) {
8754       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8755       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8756       for (c = 0; c < coneSize; ++c) {
8757         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8758         if (cone[c] != pp) {
8759           c = 0;
8760           break;
8761         }
8762         if (cone[c] == p) break;
8763       }
8764       if (c >= coneSize) {
8765         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8766         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8767         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8768         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8769         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8770         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8771         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8772       }
8773     }
8774   }
8775   if (storagecheck) {
8776     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8777     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8778     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8779   }
8780   PetscFunctionReturn(PETSC_SUCCESS);
8781 }
8782 
8783 /*
8784   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.
8785 */
8786 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8787 {
8788   DMPolytopeType  cct;
8789   PetscInt        ptpoints[4];
8790   const PetscInt *cone, *ccone, *ptcone;
8791   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8792 
8793   PetscFunctionBegin;
8794   *unsplit = 0;
8795   switch (ct) {
8796   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8797     ptpoints[npt++] = c;
8798     break;
8799   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8800     PetscCall(DMPlexGetCone(dm, c, &cone));
8801     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8802     for (cp = 0; cp < coneSize; ++cp) {
8803       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8804       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8805     }
8806     break;
8807   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8808   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8809     PetscCall(DMPlexGetCone(dm, c, &cone));
8810     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8811     for (cp = 0; cp < coneSize; ++cp) {
8812       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8813       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8814       for (ccp = 0; ccp < cconeSize; ++ccp) {
8815         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8816         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8817           PetscInt p;
8818           for (p = 0; p < npt; ++p)
8819             if (ptpoints[p] == ccone[ccp]) break;
8820           if (p == npt) ptpoints[npt++] = ccone[ccp];
8821         }
8822       }
8823     }
8824     break;
8825   default:
8826     break;
8827   }
8828   for (pt = 0; pt < npt; ++pt) {
8829     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8830     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8831   }
8832   PetscFunctionReturn(PETSC_SUCCESS);
8833 }
8834 
8835 /*@
8836   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8837 
8838   Input Parameters:
8839 + dm         - The `DMPLEX` object
8840 - cellHeight - Normally 0
8841 
8842   Level: developer
8843 
8844   Notes:
8845   This is a useful diagnostic when creating meshes programmatically.
8846   Currently applicable only to homogeneous simplex or tensor meshes.
8847 
8848   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8849 
8850 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8851 @*/
8852 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8853 {
8854   DMPlexInterpolatedFlag interp;
8855   DMPolytopeType         ct;
8856   PetscInt               vStart, vEnd, cStart, cEnd, c;
8857 
8858   PetscFunctionBegin;
8859   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8860   PetscCall(DMPlexIsInterpolated(dm, &interp));
8861   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8862   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8863   for (c = cStart; c < cEnd; ++c) {
8864     PetscInt *closure = NULL;
8865     PetscInt  coneSize, closureSize, cl, Nv = 0;
8866 
8867     PetscCall(DMPlexGetCellType(dm, c, &ct));
8868     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8869     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8870     if (interp == DMPLEX_INTERPOLATED_FULL) {
8871       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8872       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));
8873     }
8874     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8875     for (cl = 0; cl < closureSize * 2; cl += 2) {
8876       const PetscInt p = closure[cl];
8877       if ((p >= vStart) && (p < vEnd)) ++Nv;
8878     }
8879     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8880     /* Special Case: Tensor faces with identified vertices */
8881     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8882       PetscInt unsplit;
8883 
8884       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8885       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8886     }
8887     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));
8888   }
8889   PetscFunctionReturn(PETSC_SUCCESS);
8890 }
8891 
8892 /*@
8893   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8894 
8895   Collective
8896 
8897   Input Parameters:
8898 + dm         - The `DMPLEX` object
8899 - cellHeight - Normally 0
8900 
8901   Level: developer
8902 
8903   Notes:
8904   This is a useful diagnostic when creating meshes programmatically.
8905   This routine is only relevant for meshes that are fully interpolated across all ranks.
8906   It will error out if a partially interpolated mesh is given on some rank.
8907   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8908 
8909   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8910 
8911 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8912 @*/
8913 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8914 {
8915   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8916   DMPlexInterpolatedFlag interpEnum;
8917 
8918   PetscFunctionBegin;
8919   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8920   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8921   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8922   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8923     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8924     PetscFunctionReturn(PETSC_SUCCESS);
8925   }
8926 
8927   PetscCall(DMGetDimension(dm, &dim));
8928   PetscCall(DMPlexGetDepth(dm, &depth));
8929   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8930   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8931     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8932     for (c = cStart; c < cEnd; ++c) {
8933       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8934       const DMPolytopeType *faceTypes;
8935       DMPolytopeType        ct;
8936       PetscInt              numFaces, coneSize, f;
8937       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8938 
8939       PetscCall(DMPlexGetCellType(dm, c, &ct));
8940       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8941       if (unsplit) continue;
8942       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8943       PetscCall(DMPlexGetCone(dm, c, &cone));
8944       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8945       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8946       for (cl = 0; cl < closureSize * 2; cl += 2) {
8947         const PetscInt p = closure[cl];
8948         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8949       }
8950       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8951       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);
8952       for (f = 0; f < numFaces; ++f) {
8953         DMPolytopeType fct;
8954         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8955 
8956         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8957         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8958         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8959           const PetscInt p = fclosure[cl];
8960           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8961         }
8962         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]);
8963         for (v = 0; v < fnumCorners; ++v) {
8964           if (fclosure[v] != faces[fOff + v]) {
8965             PetscInt v1;
8966 
8967             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8968             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8969             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8970             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8971             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8972             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]);
8973           }
8974         }
8975         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8976         fOff += faceSizes[f];
8977       }
8978       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8979       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8980     }
8981   }
8982   PetscFunctionReturn(PETSC_SUCCESS);
8983 }
8984 
8985 /*@
8986   DMPlexCheckGeometry - Check the geometry of mesh cells
8987 
8988   Input Parameter:
8989 . dm - The `DMPLEX` object
8990 
8991   Level: developer
8992 
8993   Notes:
8994   This is a useful diagnostic when creating meshes programmatically.
8995 
8996   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8997 
8998 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8999 @*/
9000 PetscErrorCode DMPlexCheckGeometry(DM dm)
9001 {
9002   Vec       coordinates;
9003   PetscReal detJ, J[9], refVol = 1.0;
9004   PetscReal vol;
9005   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9006 
9007   PetscFunctionBegin;
9008   PetscCall(DMGetDimension(dm, &dim));
9009   PetscCall(DMGetCoordinateDim(dm, &dE));
9010   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9011   PetscCall(DMPlexGetDepth(dm, &depth));
9012   for (d = 0; d < dim; ++d) refVol *= 2.0;
9013   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9014   /* Make sure local coordinates are created, because that step is collective */
9015   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9016   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9017   for (c = cStart; c < cEnd; ++c) {
9018     DMPolytopeType ct;
9019     PetscInt       unsplit;
9020     PetscBool      ignoreZeroVol = PETSC_FALSE;
9021 
9022     PetscCall(DMPlexGetCellType(dm, c, &ct));
9023     switch (ct) {
9024     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9025     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9026     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9027       ignoreZeroVol = PETSC_TRUE;
9028       break;
9029     default:
9030       break;
9031     }
9032     switch (ct) {
9033     case DM_POLYTOPE_TRI_PRISM:
9034     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9035     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9036     case DM_POLYTOPE_PYRAMID:
9037       continue;
9038     default:
9039       break;
9040     }
9041     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9042     if (unsplit) continue;
9043     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9044     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);
9045     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9046     /* This should work with periodicity since DG coordinates should be used */
9047     if (depth > 1) {
9048       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9049       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);
9050       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9051     }
9052   }
9053   PetscFunctionReturn(PETSC_SUCCESS);
9054 }
9055 
9056 /*@
9057   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9058 
9059   Collective
9060 
9061   Input Parameters:
9062 + dm              - The `DMPLEX` object
9063 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9064 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9065 
9066   Level: developer
9067 
9068   Notes:
9069   This is mainly intended for debugging/testing purposes.
9070 
9071   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9072 
9073   Extra roots can come from priodic cuts, where additional points appear on the boundary
9074 
9075 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9076 @*/
9077 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9078 {
9079   PetscInt           l, nleaves, nroots, overlap;
9080   const PetscInt    *locals;
9081   const PetscSFNode *remotes;
9082   PetscBool          distributed;
9083   MPI_Comm           comm;
9084   PetscMPIInt        rank;
9085 
9086   PetscFunctionBegin;
9087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9088   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9089   else pointSF = dm->sf;
9090   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9091   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9092   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9093   {
9094     PetscMPIInt mpiFlag;
9095 
9096     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9097     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9098   }
9099   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9100   PetscCall(DMPlexIsDistributed(dm, &distributed));
9101   if (!distributed) {
9102     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);
9103     PetscFunctionReturn(PETSC_SUCCESS);
9104   }
9105   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);
9106   PetscCall(DMPlexGetOverlap(dm, &overlap));
9107 
9108   /* Check SF graph is compatible with DMPlex chart */
9109   {
9110     PetscInt pStart, pEnd, maxLeaf;
9111 
9112     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9113     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9114     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9115     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9116   }
9117 
9118   /* Check Point SF has no local points referenced */
9119   for (l = 0; l < nleaves; l++) {
9120     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);
9121   }
9122 
9123   /* Check there are no cells in interface */
9124   if (!overlap) {
9125     PetscInt cellHeight, cStart, cEnd;
9126 
9127     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9128     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9129     for (l = 0; l < nleaves; ++l) {
9130       const PetscInt point = locals ? locals[l] : l;
9131 
9132       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9133     }
9134   }
9135 
9136   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9137   {
9138     const PetscInt *rootdegree;
9139 
9140     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9141     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9142     for (l = 0; l < nleaves; ++l) {
9143       const PetscInt  point = locals ? locals[l] : l;
9144       const PetscInt *cone;
9145       PetscInt        coneSize, c, idx;
9146 
9147       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9148       PetscCall(DMPlexGetCone(dm, point, &cone));
9149       for (c = 0; c < coneSize; ++c) {
9150         if (!rootdegree[cone[c]]) {
9151           if (locals) {
9152             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9153           } else {
9154             idx = (cone[c] < nleaves) ? cone[c] : -1;
9155           }
9156           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9157         }
9158       }
9159     }
9160   }
9161   PetscFunctionReturn(PETSC_SUCCESS);
9162 }
9163 
9164 /*@
9165   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9166 
9167   Input Parameter:
9168 . dm - The `DMPLEX` object
9169 
9170   Level: developer
9171 
9172   Notes:
9173   This is a useful diagnostic when creating meshes programmatically.
9174 
9175   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9176 
9177   Currently does not include `DMPlexCheckCellShape()`.
9178 
9179 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9180 @*/
9181 PetscErrorCode DMPlexCheck(DM dm)
9182 {
9183   PetscInt cellHeight;
9184 
9185   PetscFunctionBegin;
9186   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9187   PetscCall(DMPlexCheckSymmetry(dm));
9188   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9189   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9190   PetscCall(DMPlexCheckGeometry(dm));
9191   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9192   PetscCall(DMPlexCheckInterfaceCones(dm));
9193   PetscFunctionReturn(PETSC_SUCCESS);
9194 }
9195 
9196 typedef struct cell_stats {
9197   PetscReal min, max, sum, squaresum;
9198   PetscInt  count;
9199 } cell_stats_t;
9200 
9201 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9202 {
9203   PetscInt i, N = *len;
9204 
9205   for (i = 0; i < N; i++) {
9206     cell_stats_t *A = (cell_stats_t *)a;
9207     cell_stats_t *B = (cell_stats_t *)b;
9208 
9209     B->min = PetscMin(A->min, B->min);
9210     B->max = PetscMax(A->max, B->max);
9211     B->sum += A->sum;
9212     B->squaresum += A->squaresum;
9213     B->count += A->count;
9214   }
9215 }
9216 
9217 /*@
9218   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9219 
9220   Collective
9221 
9222   Input Parameters:
9223 + dm        - The `DMPLEX` object
9224 . output    - If true, statistics will be displayed on `stdout`
9225 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9226 
9227   Level: developer
9228 
9229   Notes:
9230   This is mainly intended for debugging/testing purposes.
9231 
9232   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9233 
9234 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9235 @*/
9236 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9237 {
9238   DM           dmCoarse;
9239   cell_stats_t stats, globalStats;
9240   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9241   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9242   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9243   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9244   PetscMPIInt  rank, size;
9245 
9246   PetscFunctionBegin;
9247   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9248   stats.min = PETSC_MAX_REAL;
9249   stats.max = PETSC_MIN_REAL;
9250   stats.sum = stats.squaresum = 0.;
9251   stats.count                 = 0;
9252 
9253   PetscCallMPI(MPI_Comm_size(comm, &size));
9254   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9255   PetscCall(DMGetCoordinateDim(dm, &cdim));
9256   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9257   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9258   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9259   for (c = cStart; c < cEnd; c++) {
9260     PetscInt  i;
9261     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9262 
9263     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9264     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9265     for (i = 0; i < PetscSqr(cdim); ++i) {
9266       frobJ += J[i] * J[i];
9267       frobInvJ += invJ[i] * invJ[i];
9268     }
9269     cond2 = frobJ * frobInvJ;
9270     cond  = PetscSqrtReal(cond2);
9271 
9272     stats.min = PetscMin(stats.min, cond);
9273     stats.max = PetscMax(stats.max, cond);
9274     stats.sum += cond;
9275     stats.squaresum += cond2;
9276     stats.count++;
9277     if (output && cond > limit) {
9278       PetscSection coordSection;
9279       Vec          coordsLocal;
9280       PetscScalar *coords = NULL;
9281       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9282 
9283       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9284       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9285       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9286       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9287       for (i = 0; i < Nv / cdim; ++i) {
9288         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9289         for (d = 0; d < cdim; ++d) {
9290           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9291           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9292         }
9293         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9294       }
9295       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9296       for (cl = 0; cl < clSize * 2; cl += 2) {
9297         const PetscInt edge = closure[cl];
9298 
9299         if ((edge >= eStart) && (edge < eEnd)) {
9300           PetscReal len;
9301 
9302           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9303           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9304         }
9305       }
9306       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9307       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9308     }
9309   }
9310   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9311 
9312   if (size > 1) {
9313     PetscMPIInt  blockLengths[2] = {4, 1};
9314     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9315     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9316     MPI_Op       statReduce;
9317 
9318     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9319     PetscCallMPI(MPI_Type_commit(&statType));
9320     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9321     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9322     PetscCallMPI(MPI_Op_free(&statReduce));
9323     PetscCallMPI(MPI_Type_free(&statType));
9324   } else {
9325     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9326   }
9327   if (rank == 0) {
9328     count = globalStats.count;
9329     min   = globalStats.min;
9330     max   = globalStats.max;
9331     mean  = globalStats.sum / globalStats.count;
9332     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9333   }
9334 
9335   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));
9336   PetscCall(PetscFree2(J, invJ));
9337 
9338   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9339   if (dmCoarse) {
9340     PetscBool isplex;
9341 
9342     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9343     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9344   }
9345   PetscFunctionReturn(PETSC_SUCCESS);
9346 }
9347 
9348 /*@
9349   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9350   orthogonal quality below given tolerance.
9351 
9352   Collective
9353 
9354   Input Parameters:
9355 + dm   - The `DMPLEX` object
9356 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9357 - atol - [0, 1] Absolute tolerance for tagging cells.
9358 
9359   Output Parameters:
9360 + OrthQual      - `Vec` containing orthogonal quality per cell
9361 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9362 
9363   Options Database Keys:
9364 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9365 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9366 
9367   Level: intermediate
9368 
9369   Notes:
9370   Orthogonal quality is given by the following formula:
9371 
9372   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9373 
9374   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
9375   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9376   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9377   calculating the cosine of the angle between these vectors.
9378 
9379   Orthogonal quality ranges from 1 (best) to 0 (worst).
9380 
9381   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9382   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9383 
9384   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9385 
9386 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9387 @*/
9388 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9389 {
9390   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9391   PetscInt              *idx;
9392   PetscScalar           *oqVals;
9393   const PetscScalar     *cellGeomArr, *faceGeomArr;
9394   PetscReal             *ci, *fi, *Ai;
9395   MPI_Comm               comm;
9396   Vec                    cellgeom, facegeom;
9397   DM                     dmFace, dmCell;
9398   IS                     glob;
9399   ISLocalToGlobalMapping ltog;
9400   PetscViewer            vwr;
9401 
9402   PetscFunctionBegin;
9403   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9404   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9405   PetscAssertPointer(OrthQual, 4);
9406   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9407   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9408   PetscCall(DMGetDimension(dm, &nc));
9409   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9410   {
9411     DMPlexInterpolatedFlag interpFlag;
9412 
9413     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9414     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9415       PetscMPIInt rank;
9416 
9417       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9418       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9419     }
9420   }
9421   if (OrthQualLabel) {
9422     PetscAssertPointer(OrthQualLabel, 5);
9423     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9424     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9425   } else {
9426     *OrthQualLabel = NULL;
9427   }
9428   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9429   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9430   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9431   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9432   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9433   PetscCall(VecCreate(comm, OrthQual));
9434   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9435   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9436   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9437   PetscCall(VecSetUp(*OrthQual));
9438   PetscCall(ISDestroy(&glob));
9439   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9440   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9441   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9442   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9443   PetscCall(VecGetDM(cellgeom, &dmCell));
9444   PetscCall(VecGetDM(facegeom, &dmFace));
9445   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9446   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9447     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9448     PetscInt         cellarr[2], *adj = NULL;
9449     PetscScalar     *cArr, *fArr;
9450     PetscReal        minvalc = 1.0, minvalf = 1.0;
9451     PetscFVCellGeom *cg;
9452 
9453     idx[cellIter] = cell - cStart;
9454     cellarr[0]    = cell;
9455     /* Make indexing into cellGeom easier */
9456     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9457     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9458     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9459     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9460     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9461       PetscInt         i;
9462       const PetscInt   neigh  = adj[cellneigh];
9463       PetscReal        normci = 0, normfi = 0, normai = 0;
9464       PetscFVCellGeom *cgneigh;
9465       PetscFVFaceGeom *fg;
9466 
9467       /* Don't count ourselves in the neighbor list */
9468       if (neigh == cell) continue;
9469       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9470       cellarr[1] = neigh;
9471       {
9472         PetscInt        numcovpts;
9473         const PetscInt *covpts;
9474 
9475         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9476         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9477         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9478       }
9479 
9480       /* Compute c_i, f_i and their norms */
9481       for (i = 0; i < nc; i++) {
9482         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9483         fi[i] = fg->centroid[i] - cg->centroid[i];
9484         Ai[i] = fg->normal[i];
9485         normci += PetscPowReal(ci[i], 2);
9486         normfi += PetscPowReal(fi[i], 2);
9487         normai += PetscPowReal(Ai[i], 2);
9488       }
9489       normci = PetscSqrtReal(normci);
9490       normfi = PetscSqrtReal(normfi);
9491       normai = PetscSqrtReal(normai);
9492 
9493       /* Normalize and compute for each face-cell-normal pair */
9494       for (i = 0; i < nc; i++) {
9495         ci[i] = ci[i] / normci;
9496         fi[i] = fi[i] / normfi;
9497         Ai[i] = Ai[i] / normai;
9498         /* PetscAbs because I don't know if normals are guaranteed to point out */
9499         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9500         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9501       }
9502       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9503       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9504     }
9505     PetscCall(PetscFree(adj));
9506     PetscCall(PetscFree2(cArr, fArr));
9507     /* Defer to cell if they're equal */
9508     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9509     if (OrthQualLabel) {
9510       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9511     }
9512   }
9513   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9514   PetscCall(VecAssemblyBegin(*OrthQual));
9515   PetscCall(VecAssemblyEnd(*OrthQual));
9516   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9517   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9518   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9519   if (OrthQualLabel) {
9520     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9521   }
9522   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9523   PetscCall(PetscViewerDestroy(&vwr));
9524   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9525   PetscFunctionReturn(PETSC_SUCCESS);
9526 }
9527 
9528 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9529  * interpolator construction */
9530 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9531 {
9532   PetscSection section, newSection, gsection;
9533   PetscSF      sf;
9534   PetscBool    hasConstraints, ghasConstraints;
9535 
9536   PetscFunctionBegin;
9537   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9538   PetscAssertPointer(odm, 2);
9539   PetscCall(DMGetLocalSection(dm, &section));
9540   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9541   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9542   if (!ghasConstraints) {
9543     PetscCall(PetscObjectReference((PetscObject)dm));
9544     *odm = dm;
9545     PetscFunctionReturn(PETSC_SUCCESS);
9546   }
9547   PetscCall(DMClone(dm, odm));
9548   PetscCall(DMCopyFields(dm, *odm));
9549   PetscCall(DMGetLocalSection(*odm, &newSection));
9550   PetscCall(DMGetPointSF(*odm, &sf));
9551   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9552   PetscCall(DMSetGlobalSection(*odm, gsection));
9553   PetscCall(PetscSectionDestroy(&gsection));
9554   PetscFunctionReturn(PETSC_SUCCESS);
9555 }
9556 
9557 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9558 {
9559   DM        dmco, dmfo;
9560   Mat       interpo;
9561   Vec       rscale;
9562   Vec       cglobalo, clocal;
9563   Vec       fglobal, fglobalo, flocal;
9564   PetscBool regular;
9565 
9566   PetscFunctionBegin;
9567   PetscCall(DMGetFullDM(dmc, &dmco));
9568   PetscCall(DMGetFullDM(dmf, &dmfo));
9569   PetscCall(DMSetCoarseDM(dmfo, dmco));
9570   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9571   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9572   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9573   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9574   PetscCall(DMCreateLocalVector(dmc, &clocal));
9575   PetscCall(VecSet(cglobalo, 0.));
9576   PetscCall(VecSet(clocal, 0.));
9577   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9578   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9579   PetscCall(DMCreateLocalVector(dmf, &flocal));
9580   PetscCall(VecSet(fglobal, 0.));
9581   PetscCall(VecSet(fglobalo, 0.));
9582   PetscCall(VecSet(flocal, 0.));
9583   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9584   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9585   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9586   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9587   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9588   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9589   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9590   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9591   *shift = fglobal;
9592   PetscCall(VecDestroy(&flocal));
9593   PetscCall(VecDestroy(&fglobalo));
9594   PetscCall(VecDestroy(&clocal));
9595   PetscCall(VecDestroy(&cglobalo));
9596   PetscCall(VecDestroy(&rscale));
9597   PetscCall(MatDestroy(&interpo));
9598   PetscCall(DMDestroy(&dmfo));
9599   PetscCall(DMDestroy(&dmco));
9600   PetscFunctionReturn(PETSC_SUCCESS);
9601 }
9602 
9603 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9604 {
9605   PetscObject shifto;
9606   Vec         shift;
9607 
9608   PetscFunctionBegin;
9609   if (!interp) {
9610     Vec rscale;
9611 
9612     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9613     PetscCall(VecDestroy(&rscale));
9614   } else {
9615     PetscCall(PetscObjectReference((PetscObject)interp));
9616   }
9617   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9618   if (!shifto) {
9619     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9620     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9621     shifto = (PetscObject)shift;
9622     PetscCall(VecDestroy(&shift));
9623   }
9624   shift = (Vec)shifto;
9625   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9626   PetscCall(VecAXPY(fineSol, 1.0, shift));
9627   PetscCall(MatDestroy(&interp));
9628   PetscFunctionReturn(PETSC_SUCCESS);
9629 }
9630 
9631 /* Pointwise interpolation
9632      Just code FEM for now
9633      u^f = I u^c
9634      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9635      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9636      I_{ij} = psi^f_i phi^c_j
9637 */
9638 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9639 {
9640   PetscSection gsc, gsf;
9641   PetscInt     m, n;
9642   void        *ctx;
9643   DM           cdm;
9644   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9645 
9646   PetscFunctionBegin;
9647   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9648   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9649   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9650   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9651 
9652   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9653   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9654   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9655   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9656   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9657 
9658   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9659   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9660   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9661   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9662   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9663   if (scaling) {
9664     /* Use naive scaling */
9665     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9666   }
9667   PetscFunctionReturn(PETSC_SUCCESS);
9668 }
9669 
9670 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9671 {
9672   VecScatter ctx;
9673 
9674   PetscFunctionBegin;
9675   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9676   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9677   PetscCall(VecScatterDestroy(&ctx));
9678   PetscFunctionReturn(PETSC_SUCCESS);
9679 }
9680 
9681 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[])
9682 {
9683   const PetscInt Nc = uOff[1] - uOff[0];
9684   PetscInt       c;
9685   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9686 }
9687 
9688 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9689 {
9690   DM           dmc;
9691   PetscDS      ds;
9692   Vec          ones, locmass;
9693   IS           cellIS;
9694   PetscFormKey key;
9695   PetscInt     depth;
9696 
9697   PetscFunctionBegin;
9698   PetscCall(DMClone(dm, &dmc));
9699   PetscCall(DMCopyDisc(dm, dmc));
9700   PetscCall(DMGetDS(dmc, &ds));
9701   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9702   PetscCall(DMCreateGlobalVector(dmc, mass));
9703   PetscCall(DMGetLocalVector(dmc, &ones));
9704   PetscCall(DMGetLocalVector(dmc, &locmass));
9705   PetscCall(DMPlexGetDepth(dmc, &depth));
9706   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9707   PetscCall(VecSet(locmass, 0.0));
9708   PetscCall(VecSet(ones, 1.0));
9709   key.label = NULL;
9710   key.value = 0;
9711   key.field = 0;
9712   key.part  = 0;
9713   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9714   PetscCall(ISDestroy(&cellIS));
9715   PetscCall(VecSet(*mass, 0.0));
9716   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9717   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9718   PetscCall(DMRestoreLocalVector(dmc, &ones));
9719   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9720   PetscCall(DMDestroy(&dmc));
9721   PetscFunctionReturn(PETSC_SUCCESS);
9722 }
9723 
9724 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9725 {
9726   PetscSection gsc, gsf;
9727   PetscInt     m, n;
9728   void        *ctx;
9729   DM           cdm;
9730   PetscBool    regular;
9731 
9732   PetscFunctionBegin;
9733   if (dmFine == dmCoarse) {
9734     DM            dmc;
9735     PetscDS       ds;
9736     PetscWeakForm wf;
9737     Vec           u;
9738     IS            cellIS;
9739     PetscFormKey  key;
9740     PetscInt      depth;
9741 
9742     PetscCall(DMClone(dmFine, &dmc));
9743     PetscCall(DMCopyDisc(dmFine, dmc));
9744     PetscCall(DMGetDS(dmc, &ds));
9745     PetscCall(PetscDSGetWeakForm(ds, &wf));
9746     PetscCall(PetscWeakFormClear(wf));
9747     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9748     PetscCall(DMCreateMatrix(dmc, mass));
9749     PetscCall(DMGetLocalVector(dmc, &u));
9750     PetscCall(DMPlexGetDepth(dmc, &depth));
9751     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9752     PetscCall(MatZeroEntries(*mass));
9753     key.label = NULL;
9754     key.value = 0;
9755     key.field = 0;
9756     key.part  = 0;
9757     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9758     PetscCall(ISDestroy(&cellIS));
9759     PetscCall(DMRestoreLocalVector(dmc, &u));
9760     PetscCall(DMDestroy(&dmc));
9761   } else {
9762     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9763     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9764     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9765     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9766 
9767     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9768     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9769     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9770     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9771 
9772     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9773     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9774     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9775     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9776   }
9777   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9778   PetscFunctionReturn(PETSC_SUCCESS);
9779 }
9780 
9781 /*@
9782   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9783 
9784   Input Parameter:
9785 . dm - The `DMPLEX` object
9786 
9787   Output Parameter:
9788 . regular - The flag
9789 
9790   Level: intermediate
9791 
9792 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9793 @*/
9794 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9795 {
9796   PetscFunctionBegin;
9797   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9798   PetscAssertPointer(regular, 2);
9799   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9800   PetscFunctionReturn(PETSC_SUCCESS);
9801 }
9802 
9803 /*@
9804   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9805 
9806   Input Parameters:
9807 + dm      - The `DMPLEX` object
9808 - regular - The flag
9809 
9810   Level: intermediate
9811 
9812 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9813 @*/
9814 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9815 {
9816   PetscFunctionBegin;
9817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9818   ((DM_Plex *)dm->data)->regularRefinement = regular;
9819   PetscFunctionReturn(PETSC_SUCCESS);
9820 }
9821 
9822 /*@
9823   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9824   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9825 
9826   Not Collective
9827 
9828   Input Parameter:
9829 . dm - The `DMPLEX` object
9830 
9831   Output Parameters:
9832 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9833 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9834 
9835   Level: intermediate
9836 
9837 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9838 @*/
9839 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9840 {
9841   DM_Plex *plex = (DM_Plex *)dm->data;
9842 
9843   PetscFunctionBegin;
9844   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9845   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9846   if (anchorSection) *anchorSection = plex->anchorSection;
9847   if (anchorIS) *anchorIS = plex->anchorIS;
9848   PetscFunctionReturn(PETSC_SUCCESS);
9849 }
9850 
9851 /*@
9852   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9853   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9854   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9855 
9856   Collective
9857 
9858   Input Parameters:
9859 + dm            - The `DMPLEX` object
9860 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9861                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9862 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9863 
9864   Level: intermediate
9865 
9866   Notes:
9867   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9868   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9869 
9870   The reference counts of `anchorSection` and `anchorIS` are incremented.
9871 
9872 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9873 @*/
9874 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9875 {
9876   DM_Plex    *plex = (DM_Plex *)dm->data;
9877   PetscMPIInt result;
9878 
9879   PetscFunctionBegin;
9880   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9881   if (anchorSection) {
9882     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9883     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9884     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9885   }
9886   if (anchorIS) {
9887     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9888     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9889     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9890   }
9891 
9892   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9893   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9894   plex->anchorSection = anchorSection;
9895 
9896   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9897   PetscCall(ISDestroy(&plex->anchorIS));
9898   plex->anchorIS = anchorIS;
9899 
9900   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9901     PetscInt        size, a, pStart, pEnd;
9902     const PetscInt *anchors;
9903 
9904     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9905     PetscCall(ISGetLocalSize(anchorIS, &size));
9906     PetscCall(ISGetIndices(anchorIS, &anchors));
9907     for (a = 0; a < size; a++) {
9908       PetscInt p;
9909 
9910       p = anchors[a];
9911       if (p >= pStart && p < pEnd) {
9912         PetscInt dof;
9913 
9914         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9915         if (dof) {
9916           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9917           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9918         }
9919       }
9920     }
9921     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9922   }
9923   /* reset the generic constraints */
9924   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9925   PetscFunctionReturn(PETSC_SUCCESS);
9926 }
9927 
9928 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9929 {
9930   PetscSection anchorSection;
9931   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9932 
9933   PetscFunctionBegin;
9934   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9935   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9936   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9937   PetscCall(PetscSectionGetNumFields(section, &numFields));
9938   if (numFields) {
9939     PetscInt f;
9940     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9941 
9942     for (f = 0; f < numFields; f++) {
9943       PetscInt numComp;
9944 
9945       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9946       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9947     }
9948   }
9949   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9950   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9951   pStart = PetscMax(pStart, sStart);
9952   pEnd   = PetscMin(pEnd, sEnd);
9953   pEnd   = PetscMax(pStart, pEnd);
9954   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9955   for (p = pStart; p < pEnd; p++) {
9956     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9957     if (dof) {
9958       PetscCall(PetscSectionGetDof(section, p, &dof));
9959       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9960       for (f = 0; f < numFields; f++) {
9961         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9962         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9963       }
9964     }
9965   }
9966   PetscCall(PetscSectionSetUp(*cSec));
9967   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9968   PetscFunctionReturn(PETSC_SUCCESS);
9969 }
9970 
9971 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9972 {
9973   PetscSection    aSec;
9974   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9975   const PetscInt *anchors;
9976   PetscInt        numFields, f;
9977   IS              aIS;
9978   MatType         mtype;
9979   PetscBool       iscuda, iskokkos;
9980 
9981   PetscFunctionBegin;
9982   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9983   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9984   PetscCall(PetscSectionGetStorageSize(section, &n));
9985   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9986   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9987   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9988   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9989   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9990   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9991   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9992   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9993   else mtype = MATSEQAIJ;
9994   PetscCall(MatSetType(*cMat, mtype));
9995   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9996   PetscCall(ISGetIndices(aIS, &anchors));
9997   /* cSec will be a subset of aSec and section */
9998   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9999   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10000   PetscCall(PetscMalloc1(m + 1, &i));
10001   i[0] = 0;
10002   PetscCall(PetscSectionGetNumFields(section, &numFields));
10003   for (p = pStart; p < pEnd; p++) {
10004     PetscInt rDof, rOff, r;
10005 
10006     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10007     if (!rDof) continue;
10008     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10009     if (numFields) {
10010       for (f = 0; f < numFields; f++) {
10011         annz = 0;
10012         for (r = 0; r < rDof; r++) {
10013           a = anchors[rOff + r];
10014           if (a < sStart || a >= sEnd) continue;
10015           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10016           annz += aDof;
10017         }
10018         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10019         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10020         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10021       }
10022     } else {
10023       annz = 0;
10024       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10025       for (q = 0; q < dof; q++) {
10026         a = anchors[rOff + q];
10027         if (a < sStart || a >= sEnd) continue;
10028         PetscCall(PetscSectionGetDof(section, a, &aDof));
10029         annz += aDof;
10030       }
10031       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10032       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10033       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10034     }
10035   }
10036   nnz = i[m];
10037   PetscCall(PetscMalloc1(nnz, &j));
10038   offset = 0;
10039   for (p = pStart; p < pEnd; p++) {
10040     if (numFields) {
10041       for (f = 0; f < numFields; f++) {
10042         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10043         for (q = 0; q < dof; q++) {
10044           PetscInt rDof, rOff, r;
10045           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10046           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10047           for (r = 0; r < rDof; r++) {
10048             PetscInt s;
10049 
10050             a = anchors[rOff + r];
10051             if (a < sStart || a >= sEnd) continue;
10052             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10053             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10054             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10055           }
10056         }
10057       }
10058     } else {
10059       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10060       for (q = 0; q < dof; q++) {
10061         PetscInt rDof, rOff, r;
10062         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10063         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10064         for (r = 0; r < rDof; r++) {
10065           PetscInt s;
10066 
10067           a = anchors[rOff + r];
10068           if (a < sStart || a >= sEnd) continue;
10069           PetscCall(PetscSectionGetDof(section, a, &aDof));
10070           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10071           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10072         }
10073       }
10074     }
10075   }
10076   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10077   PetscCall(PetscFree(i));
10078   PetscCall(PetscFree(j));
10079   PetscCall(ISRestoreIndices(aIS, &anchors));
10080   PetscFunctionReturn(PETSC_SUCCESS);
10081 }
10082 
10083 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10084 {
10085   DM_Plex     *plex = (DM_Plex *)dm->data;
10086   PetscSection anchorSection, section, cSec;
10087   Mat          cMat;
10088 
10089   PetscFunctionBegin;
10090   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10091   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10092   if (anchorSection) {
10093     PetscInt Nf;
10094 
10095     PetscCall(DMGetLocalSection(dm, &section));
10096     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10097     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10098     PetscCall(DMGetNumFields(dm, &Nf));
10099     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10100     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10101     PetscCall(PetscSectionDestroy(&cSec));
10102     PetscCall(MatDestroy(&cMat));
10103   }
10104   PetscFunctionReturn(PETSC_SUCCESS);
10105 }
10106 
10107 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10108 {
10109   IS           subis;
10110   PetscSection section, subsection;
10111 
10112   PetscFunctionBegin;
10113   PetscCall(DMGetLocalSection(dm, &section));
10114   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10115   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10116   /* Create subdomain */
10117   PetscCall(DMPlexFilter(dm, label, value, subdm));
10118   /* Create submodel */
10119   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10120   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10121   PetscCall(DMSetLocalSection(*subdm, subsection));
10122   PetscCall(PetscSectionDestroy(&subsection));
10123   PetscCall(DMCopyDisc(dm, *subdm));
10124   /* Create map from submodel to global model */
10125   if (is) {
10126     PetscSection    sectionGlobal, subsectionGlobal;
10127     IS              spIS;
10128     const PetscInt *spmap;
10129     PetscInt       *subIndices;
10130     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10131     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10132 
10133     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10134     PetscCall(ISGetIndices(spIS, &spmap));
10135     PetscCall(PetscSectionGetNumFields(section, &Nf));
10136     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10137     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10138     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10139     for (p = pStart; p < pEnd; ++p) {
10140       PetscInt gdof, pSubSize = 0;
10141 
10142       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10143       if (gdof > 0) {
10144         for (f = 0; f < Nf; ++f) {
10145           PetscInt fdof, fcdof;
10146 
10147           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10148           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10149           pSubSize += fdof - fcdof;
10150         }
10151         subSize += pSubSize;
10152         if (pSubSize) {
10153           if (bs < 0) {
10154             bs = pSubSize;
10155           } else if (bs != pSubSize) {
10156             /* Layout does not admit a pointwise block size */
10157             bs = 1;
10158           }
10159         }
10160       }
10161     }
10162     /* Must have same blocksize on all procs (some might have no points) */
10163     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10164     bsLocal[1] = bs;
10165     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10166     if (bsMinMax[0] != bsMinMax[1]) {
10167       bs = 1;
10168     } else {
10169       bs = bsMinMax[0];
10170     }
10171     PetscCall(PetscMalloc1(subSize, &subIndices));
10172     for (p = pStart; p < pEnd; ++p) {
10173       PetscInt gdof, goff;
10174 
10175       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10176       if (gdof > 0) {
10177         const PetscInt point = spmap[p];
10178 
10179         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10180         for (f = 0; f < Nf; ++f) {
10181           PetscInt fdof, fcdof, fc, f2, poff = 0;
10182 
10183           /* Can get rid of this loop by storing field information in the global section */
10184           for (f2 = 0; f2 < f; ++f2) {
10185             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10186             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10187             poff += fdof - fcdof;
10188           }
10189           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10190           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10191           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10192         }
10193       }
10194     }
10195     PetscCall(ISRestoreIndices(spIS, &spmap));
10196     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10197     if (bs > 1) {
10198       /* We need to check that the block size does not come from non-contiguous fields */
10199       PetscInt i, j, set = 1;
10200       for (i = 0; i < subSize; i += bs) {
10201         for (j = 0; j < bs; ++j) {
10202           if (subIndices[i + j] != subIndices[i] + j) {
10203             set = 0;
10204             break;
10205           }
10206         }
10207       }
10208       if (set) PetscCall(ISSetBlockSize(*is, bs));
10209     }
10210     /* Attach nullspace */
10211     for (f = 0; f < Nf; ++f) {
10212       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10213       if ((*subdm)->nullspaceConstructors[f]) break;
10214     }
10215     if (f < Nf) {
10216       MatNullSpace nullSpace;
10217       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10218 
10219       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10220       PetscCall(MatNullSpaceDestroy(&nullSpace));
10221     }
10222   }
10223   PetscFunctionReturn(PETSC_SUCCESS);
10224 }
10225 
10226 /*@
10227   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10228 
10229   Input Parameters:
10230 + dm    - The `DM`
10231 - dummy - unused argument
10232 
10233   Options Database Key:
10234 . -dm_plex_monitor_throughput - Activate the monitor
10235 
10236   Level: developer
10237 
10238 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10239 @*/
10240 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10241 {
10242   PetscLogHandler default_handler;
10243 
10244   PetscFunctionBegin;
10245   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10246   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10247   if (default_handler) {
10248     PetscLogEvent      event;
10249     PetscEventPerfInfo eventInfo;
10250     PetscReal          cellRate, flopRate;
10251     PetscInt           cStart, cEnd, Nf, N;
10252     const char        *name;
10253 
10254     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10255     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10256     PetscCall(DMGetNumFields(dm, &Nf));
10257     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10258     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10259     N        = (cEnd - cStart) * Nf * eventInfo.count;
10260     flopRate = eventInfo.flops / eventInfo.time;
10261     cellRate = N / eventInfo.time;
10262     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)));
10263   } else {
10264     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off or the default log handler is not running. Reconfigure using --with-log and run with -log_view.");
10265   }
10266   PetscFunctionReturn(PETSC_SUCCESS);
10267 }
10268