xref: /petsc/src/dm/impls/plex/plex.c (revision 172ee2662f600bbadcf24cd1159ee4392d8f8483) !
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()`, `DMPlexGetGhostCellStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(PETSC_SUCCESS);
106 }
107 
108 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
109 {
110   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
111   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
112 
113   PetscFunctionBegin;
114   *ft = PETSC_VTK_INVALID;
115   PetscCall(DMGetCoordinateDim(dm, &cdim));
116   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
118   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
119   if (field >= 0) {
120     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
121     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
122   } else {
123     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
124     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
125   }
126   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(PETSC_SUCCESS);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective
154 
155   Input Parameters:
156 + dm - The `DMPLEX` object
157 . n  - The number of vectors
158 . u  - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
202         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
203         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(PETSC_SUCCESS);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *array;
253   PetscReal          lbound[3], ubound[3];
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
278   PetscCall(PetscDrawClear(draw));
279 
280   /* Could implement something like DMDASelectFields() */
281   for (f = 0; f < Nf; ++f) {
282     DM          fdm = dm;
283     Vec         fv  = v;
284     IS          fis;
285     char        prefix[PETSC_MAX_PATH_LEN];
286     const char *fname;
287 
288     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
289     PetscCall(PetscSectionGetFieldName(s, f, &fname));
290 
291     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
292     else prefix[0] = '\0';
293     if (Nf > 1) {
294       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
295       PetscCall(VecGetSubVector(v, fis, &fv));
296       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
297       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
298     }
299     for (comp = 0; comp < Nc; ++comp, ++w) {
300       PetscInt nmax = 2;
301 
302       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
303       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
304       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
305       PetscCall(PetscDrawSetTitle(draw, title));
306 
307       /* TODO Get max and min only for this component */
308       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
309       if (!flg) {
310         PetscCall(VecMin(fv, NULL, &vbound[0]));
311         PetscCall(VecMax(fv, NULL, &vbound[1]));
312         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
313       }
314 
315       PetscCall(PetscDrawGetPopup(draw, &popup));
316       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
317       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
318       PetscCall(VecGetArrayRead(fv, &array));
319       for (c = cStart; c < cEnd; ++c) {
320         PetscScalar       *coords = NULL, *a = NULL;
321         const PetscScalar *coords_arr;
322         PetscBool          isDG;
323         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
324 
325         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
326         if (a) {
327           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
328           color[1] = color[2] = color[3] = color[0];
329         } else {
330           PetscScalar *vals = NULL;
331           PetscInt     numVals, va;
332 
333           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
334           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
335           switch (numVals / Nc) {
336           case 3: /* P1 Triangle */
337           case 4: /* P1 Quadrangle */
338             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
339             break;
340           case 6: /* P2 Triangle */
341           case 8: /* P2 Quadrangle */
342             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
343             break;
344           default:
345             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
346           }
347           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
348         }
349         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
350         switch (numCoords) {
351         case 6:
352         case 12: /* Localized triangle */
353           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
354           break;
355         case 8:
356         case 16: /* Localized quadrilateral */
357           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
358           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
359           break;
360         default:
361           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
362         }
363         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
364       }
365       PetscCall(VecRestoreArrayRead(fv, &array));
366       PetscCall(PetscDrawFlush(draw));
367       PetscCall(PetscDrawPause(draw));
368       PetscCall(PetscDrawSave(draw));
369     }
370     if (Nf > 1) {
371       PetscCall(VecRestoreSubVector(v, fis, &fv));
372       PetscCall(ISDestroy(&fis));
373       PetscCall(DMDestroy(&fdm));
374     }
375   }
376   PetscFunctionReturn(PETSC_SUCCESS);
377 }
378 
379 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
380 {
381   DM        dm;
382   PetscDraw draw;
383   PetscInt  dim;
384   PetscBool isnull;
385 
386   PetscFunctionBegin;
387   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
388   PetscCall(PetscDrawIsNull(draw, &isnull));
389   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
390 
391   PetscCall(VecGetDM(v, &dm));
392   PetscCall(DMGetCoordinateDim(dm, &dim));
393   switch (dim) {
394   case 1:
395     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
396     break;
397   case 2:
398     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
399     break;
400   default:
401     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
402   }
403   PetscFunctionReturn(PETSC_SUCCESS);
404 }
405 
406 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
407 {
408   DM                      dm;
409   Vec                     locv;
410   const char             *name;
411   PetscSection            section;
412   PetscInt                pStart, pEnd;
413   PetscInt                numFields;
414   PetscViewerVTKFieldType ft;
415 
416   PetscFunctionBegin;
417   PetscCall(VecGetDM(v, &dm));
418   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
419   PetscCall(PetscObjectGetName((PetscObject)v, &name));
420   PetscCall(PetscObjectSetName((PetscObject)locv, name));
421   PetscCall(VecCopy(v, locv));
422   PetscCall(DMGetLocalSection(dm, &section));
423   PetscCall(PetscSectionGetNumFields(section, &numFields));
424   if (!numFields) {
425     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
426     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
427   } else {
428     PetscInt f;
429 
430     for (f = 0; f < numFields; f++) {
431       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
432       if (ft == PETSC_VTK_INVALID) continue;
433       PetscCall(PetscObjectReference((PetscObject)locv));
434       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
435     }
436     PetscCall(VecDestroy(&locv));
437   }
438   PetscFunctionReturn(PETSC_SUCCESS);
439 }
440 
441 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
442 {
443   DM        dm;
444   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
445 
446   PetscFunctionBegin;
447   PetscCall(VecGetDM(v, &dm));
448   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
449   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
450   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
453   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
454   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
455     PetscInt    i, numFields;
456     PetscObject fe;
457     PetscBool   fem  = PETSC_FALSE;
458     Vec         locv = v;
459     const char *name;
460     PetscInt    step;
461     PetscReal   time;
462 
463     PetscCall(DMGetNumFields(dm, &numFields));
464     for (i = 0; i < numFields; i++) {
465       PetscCall(DMGetField(dm, i, NULL, &fe));
466       if (fe->classid == PETSCFE_CLASSID) {
467         fem = PETSC_TRUE;
468         break;
469       }
470     }
471     if (fem) {
472       PetscObject isZero;
473 
474       PetscCall(DMGetLocalVector(dm, &locv));
475       PetscCall(PetscObjectGetName((PetscObject)v, &name));
476       PetscCall(PetscObjectSetName((PetscObject)locv, name));
477       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
478       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
479       PetscCall(VecCopy(v, locv));
480       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
481       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
482     }
483     if (isvtk) {
484       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
485     } else if (ishdf5) {
486 #if defined(PETSC_HAVE_HDF5)
487       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
488 #else
489       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
490 #endif
491     } else if (isdraw) {
492       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
493     } else if (isglvis) {
494       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
495       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
496       PetscCall(VecView_GLVis(locv, viewer));
497     } else if (iscgns) {
498 #if defined(PETSC_HAVE_CGNS)
499       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
500 #else
501       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
502 #endif
503     }
504     if (fem) {
505       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
506       PetscCall(DMRestoreLocalVector(dm, &locv));
507     }
508   } else {
509     PetscBool isseq;
510 
511     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
512     if (isseq) PetscCall(VecView_Seq(v, viewer));
513     else PetscCall(VecView_MPI(v, viewer));
514   }
515   PetscFunctionReturn(PETSC_SUCCESS);
516 }
517 
518 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
519 {
520   DM        dm;
521   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
527   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
528   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
529   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
532   if (isvtk || isdraw || isglvis || iscgns) {
533     Vec         locv;
534     PetscObject isZero;
535     const char *name;
536 
537     PetscCall(DMGetLocalVector(dm, &locv));
538     PetscCall(PetscObjectGetName((PetscObject)v, &name));
539     PetscCall(PetscObjectSetName((PetscObject)locv, name));
540     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
541     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
542     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
543     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
544     PetscCall(VecView_Plex_Local(locv, viewer));
545     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
546     PetscCall(DMRestoreLocalVector(dm, &locv));
547   } else if (ishdf5) {
548 #if defined(PETSC_HAVE_HDF5)
549     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
550 #else
551     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
552 #endif
553   } else if (isexodusii) {
554 #if defined(PETSC_HAVE_EXODUSII)
555     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
556 #else
557     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
558 #endif
559   } else {
560     PetscBool isseq;
561 
562     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
563     if (isseq) PetscCall(VecView_Seq(v, viewer));
564     else PetscCall(VecView_MPI(v, viewer));
565   }
566   PetscFunctionReturn(PETSC_SUCCESS);
567 }
568 
569 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
570 {
571   DM                dm;
572   MPI_Comm          comm;
573   PetscViewerFormat format;
574   Vec               v;
575   PetscBool         isvtk, ishdf5;
576 
577   PetscFunctionBegin;
578   PetscCall(VecGetDM(originalv, &dm));
579   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
580   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
581   PetscCall(PetscViewerGetFormat(viewer, &format));
582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
583   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
584   if (format == PETSC_VIEWER_NATIVE) {
585     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
586     /* this need a better fix */
587     if (dm->useNatural) {
588       if (dm->sfNatural) {
589         const char *vecname;
590         PetscInt    n, nroots;
591 
592         PetscCall(VecGetLocalSize(originalv, &n));
593         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
594         if (n == nroots) {
595           PetscCall(DMPlexCreateNaturalVector(dm, &v));
596           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
597           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
598           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
599           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
600         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
601       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
602     } else v = originalv;
603   } else v = originalv;
604 
605   if (ishdf5) {
606 #if defined(PETSC_HAVE_HDF5)
607     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
608 #else
609     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
610 #endif
611   } else if (isvtk) {
612     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
613   } else {
614     PetscBool isseq;
615 
616     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
617     if (isseq) PetscCall(VecView_Seq(v, viewer));
618     else PetscCall(VecView_MPI(v, viewer));
619   }
620   if (v != originalv) PetscCall(VecDestroy(&v));
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
625 {
626   DM        dm;
627   PetscBool ishdf5;
628 
629   PetscFunctionBegin;
630   PetscCall(VecGetDM(v, &dm));
631   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
633   if (ishdf5) {
634     DM          dmBC;
635     Vec         gv;
636     const char *name;
637 
638     PetscCall(DMGetOutputDM(dm, &dmBC));
639     PetscCall(DMGetGlobalVector(dmBC, &gv));
640     PetscCall(PetscObjectGetName((PetscObject)v, &name));
641     PetscCall(PetscObjectSetName((PetscObject)gv, name));
642     PetscCall(VecLoad_Default(gv, viewer));
643     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
644     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
645     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
646   } else PetscCall(VecLoad_Default(v, viewer));
647   PetscFunctionReturn(PETSC_SUCCESS);
648 }
649 
650 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
651 {
652   DM        dm;
653   PetscBool ishdf5, isexodusii;
654 
655   PetscFunctionBegin;
656   PetscCall(VecGetDM(v, &dm));
657   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
659   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
660   if (ishdf5) {
661 #if defined(PETSC_HAVE_HDF5)
662     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
665 #endif
666   } else if (isexodusii) {
667 #if defined(PETSC_HAVE_EXODUSII)
668     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
669 #else
670     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
671 #endif
672   } else PetscCall(VecLoad_Default(v, viewer));
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   PetscViewerFormat format;
680   PetscBool         ishdf5;
681 
682   PetscFunctionBegin;
683   PetscCall(VecGetDM(originalv, &dm));
684   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
685   PetscCall(PetscViewerGetFormat(viewer, &format));
686   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
687   if (format == PETSC_VIEWER_NATIVE) {
688     if (dm->useNatural) {
689       if (dm->sfNatural) {
690         if (ishdf5) {
691 #if defined(PETSC_HAVE_HDF5)
692           Vec         v;
693           const char *vecname;
694 
695           PetscCall(DMPlexCreateNaturalVector(dm, &v));
696           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
697           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
698           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
699           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
700           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
701           PetscCall(VecDestroy(&v));
702 #else
703           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
704 #endif
705         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
706       }
707     } else PetscCall(VecLoad_Default(originalv, viewer));
708   }
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
713 {
714   PetscSection       coordSection;
715   Vec                coordinates;
716   DMLabel            depthLabel, celltypeLabel;
717   const char        *name[4];
718   const PetscScalar *a;
719   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
720 
721   PetscFunctionBegin;
722   PetscCall(DMGetDimension(dm, &dim));
723   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
724   PetscCall(DMGetCoordinateSection(dm, &coordSection));
725   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
726   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
727   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
728   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
729   PetscCall(VecGetArrayRead(coordinates, &a));
730   name[0]       = "vertex";
731   name[1]       = "edge";
732   name[dim - 1] = "face";
733   name[dim]     = "cell";
734   for (c = cStart; c < cEnd; ++c) {
735     PetscInt *closure = NULL;
736     PetscInt  closureSize, cl, ct;
737 
738     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
739     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
740     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
741     PetscCall(PetscViewerASCIIPushTab(viewer));
742     for (cl = 0; cl < closureSize * 2; cl += 2) {
743       PetscInt point = closure[cl], depth, dof, off, d, p;
744 
745       if ((point < pStart) || (point >= pEnd)) continue;
746       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
747       if (!dof) continue;
748       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
749       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
750       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
751       for (p = 0; p < dof / dim; ++p) {
752         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
753         for (d = 0; d < dim; ++d) {
754           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
755           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
756         }
757         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
758       }
759       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
760     }
761     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
762     PetscCall(PetscViewerASCIIPopTab(viewer));
763   }
764   PetscCall(VecRestoreArrayRead(coordinates, &a));
765   PetscFunctionReturn(PETSC_SUCCESS);
766 }
767 
768 typedef enum {
769   CS_CARTESIAN,
770   CS_POLAR,
771   CS_CYLINDRICAL,
772   CS_SPHERICAL
773 } CoordSystem;
774 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
775 
776 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
777 {
778   PetscInt i;
779 
780   PetscFunctionBegin;
781   if (dim > 3) {
782     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
783   } else {
784     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
785 
786     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
787     switch (cs) {
788     case CS_CARTESIAN:
789       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
790       break;
791     case CS_POLAR:
792       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
793       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
794       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
795       break;
796     case CS_CYLINDRICAL:
797       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       trcoords[2] = coords[2];
801       break;
802     case CS_SPHERICAL:
803       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
804       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
805       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
806       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
807       break;
808     }
809     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
810   }
811   PetscFunctionReturn(PETSC_SUCCESS);
812 }
813 
814 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
815 {
816   DM_Plex          *mesh = (DM_Plex *)dm->data;
817   DM                cdm, cdmCell;
818   PetscSection      coordSection, coordSectionCell;
819   Vec               coordinates, coordinatesCell;
820   PetscViewerFormat format;
821 
822   PetscFunctionBegin;
823   PetscCall(PetscViewerGetFormat(viewer, &format));
824   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
825     const char *name;
826     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
827     PetscInt    pStart, pEnd, p, numLabels, l;
828     PetscMPIInt rank, size;
829 
830     PetscCall(DMGetCoordinateDM(dm, &cdm));
831     PetscCall(DMGetCoordinateSection(dm, &coordSection));
832     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
833     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
834     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
835     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
836     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
837     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
838     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
839     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
840     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
841     PetscCall(DMGetDimension(dm, &dim));
842     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
843     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
844     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
845     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
847     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
848     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
849     for (p = pStart; p < pEnd; ++p) {
850       PetscInt dof, off, s;
851 
852       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
853       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
854       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
855     }
856     PetscCall(PetscViewerFlush(viewer));
857     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
858     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
859     for (p = pStart; p < pEnd; ++p) {
860       PetscInt dof, off, c;
861 
862       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
863       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
864       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
865     }
866     PetscCall(PetscViewerFlush(viewer));
867     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
868     if (coordSection && coordinates) {
869       CoordSystem        cs = CS_CARTESIAN;
870       const PetscScalar *array, *arrayCell = NULL;
871       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
872       PetscMPIInt        rank;
873       const char        *name;
874 
875       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
876       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
877       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
878       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
879       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
880       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
881       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
882       pStart = PetscMin(pvStart, pcStart);
883       pEnd   = PetscMax(pvEnd, pcEnd);
884       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
885       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
886       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
887       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
888 
889       PetscCall(VecGetArrayRead(coordinates, &array));
890       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
891       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
892       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
893       for (p = pStart; p < pEnd; ++p) {
894         PetscInt dof, off;
895 
896         if (p >= pvStart && p < pvEnd) {
897           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
898           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
899           if (dof) {
900             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
901             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
902             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
903           }
904         }
905         if (cdmCell && p >= pcStart && p < pcEnd) {
906           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
907           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
908           if (dof) {
909             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
910             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
911             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
912           }
913         }
914       }
915       PetscCall(PetscViewerFlush(viewer));
916       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
917       PetscCall(VecRestoreArrayRead(coordinates, &array));
918       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
919     }
920     PetscCall(DMGetNumLabels(dm, &numLabels));
921     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
922     for (l = 0; l < numLabels; ++l) {
923       DMLabel     label;
924       PetscBool   isdepth;
925       const char *name;
926 
927       PetscCall(DMGetLabelName(dm, l, &name));
928       PetscCall(PetscStrcmp(name, "depth", &isdepth));
929       if (isdepth) continue;
930       PetscCall(DMGetLabel(dm, name, &label));
931       PetscCall(DMLabelView(label, viewer));
932     }
933     if (size > 1) {
934       PetscSF sf;
935 
936       PetscCall(DMGetPointSF(dm, &sf));
937       PetscCall(PetscSFView(sf, viewer));
938     }
939     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
940     PetscCall(PetscViewerFlush(viewer));
941   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
942     const char  *name, *color;
943     const char  *defcolors[3]  = {"gray", "orange", "green"};
944     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
945     char         lname[PETSC_MAX_PATH_LEN];
946     PetscReal    scale      = 2.0;
947     PetscReal    tikzscale  = 1.0;
948     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
949     double       tcoords[3];
950     PetscScalar *coords;
951     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
952     PetscMPIInt  rank, size;
953     char       **names, **colors, **lcolors;
954     PetscBool    flg, lflg;
955     PetscBT      wp = NULL;
956     PetscInt     pEnd, pStart;
957 
958     PetscCall(DMGetCoordinateDM(dm, &cdm));
959     PetscCall(DMGetCoordinateSection(dm, &coordSection));
960     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
961     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
962     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
963     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
964     PetscCall(DMGetDimension(dm, &dim));
965     PetscCall(DMPlexGetDepth(dm, &depth));
966     PetscCall(DMGetNumLabels(dm, &numLabels));
967     numLabels  = PetscMax(numLabels, 10);
968     numColors  = 10;
969     numLColors = 10;
970     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
971     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
972     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
973     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
974     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
975     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
976     n = 4;
977     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
978     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
979     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
980     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
981     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
982     if (!useLabels) numLabels = 0;
983     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
984     if (!useColors) {
985       numColors = 3;
986       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
987     }
988     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
989     if (!useColors) {
990       numLColors = 4;
991       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
992     }
993     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
994     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
995     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
996     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
997     if (depth < dim) plotEdges = PETSC_FALSE;
998     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
999 
1000     /* filter points with labelvalue != labeldefaultvalue */
1001     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1002     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1003     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1004     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1005     if (lflg) {
1006       DMLabel lbl;
1007 
1008       PetscCall(DMGetLabel(dm, lname, &lbl));
1009       if (lbl) {
1010         PetscInt val, defval;
1011 
1012         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1013         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1014         for (c = pStart; c < pEnd; c++) {
1015           PetscInt *closure = NULL;
1016           PetscInt  closureSize;
1017 
1018           PetscCall(DMLabelGetValue(lbl, c, &val));
1019           if (val == defval) continue;
1020 
1021           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1022           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1023           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1024         }
1025       }
1026     }
1027 
1028     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1029     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1030     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1031     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1032 \\documentclass[tikz]{standalone}\n\n\
1033 \\usepackage{pgflibraryshapes}\n\
1034 \\usetikzlibrary{backgrounds}\n\
1035 \\usetikzlibrary{arrows}\n\
1036 \\begin{document}\n"));
1037     if (size > 1) {
1038       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1039       for (p = 0; p < size; ++p) {
1040         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1041         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1042       }
1043       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1044     }
1045     if (drawHasse) {
1046       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1047 
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1060     }
1061     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1062 
1063     /* Plot vertices */
1064     PetscCall(VecGetArray(coordinates, &coords));
1065     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1066     for (v = vStart; v < vEnd; ++v) {
1067       PetscInt  off, dof, d;
1068       PetscBool isLabeled = PETSC_FALSE;
1069 
1070       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1071       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1072       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1073       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1074       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1075       for (d = 0; d < dof; ++d) {
1076         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1077         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1078       }
1079       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1080       if (dim == 3) {
1081         PetscReal tmp = tcoords[1];
1082         tcoords[1]    = tcoords[2];
1083         tcoords[2]    = -tmp;
1084       }
1085       for (d = 0; d < dof; ++d) {
1086         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1088       }
1089       if (drawHasse) color = colors[0 % numColors];
1090       else color = colors[rank % numColors];
1091       for (l = 0; l < numLabels; ++l) {
1092         PetscInt val;
1093         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1094         if (val >= 0) {
1095           color     = lcolors[l % numLColors];
1096           isLabeled = PETSC_TRUE;
1097           break;
1098         }
1099       }
1100       if (drawNumbers[0]) {
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1102       } else if (drawColors[0]) {
1103         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1104       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1105     }
1106     PetscCall(VecRestoreArray(coordinates, &coords));
1107     PetscCall(PetscViewerFlush(viewer));
1108     /* Plot edges */
1109     if (plotEdges) {
1110       PetscCall(VecGetArray(coordinates, &coords));
1111       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1112       for (e = eStart; e < eEnd; ++e) {
1113         const PetscInt *cone;
1114         PetscInt        coneSize, offA, offB, dof, d;
1115 
1116         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1117         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1118         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1119         PetscCall(DMPlexGetCone(dm, e, &cone));
1120         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1121         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1122         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1123         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1124         for (d = 0; d < dof; ++d) {
1125           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1126           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1127         }
1128         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1129         if (dim == 3) {
1130           PetscReal tmp = tcoords[1];
1131           tcoords[1]    = tcoords[2];
1132           tcoords[2]    = -tmp;
1133         }
1134         for (d = 0; d < dof; ++d) {
1135           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1136           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1137         }
1138         if (drawHasse) color = colors[1 % numColors];
1139         else color = colors[rank % numColors];
1140         for (l = 0; l < numLabels; ++l) {
1141           PetscInt val;
1142           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1143           if (val >= 0) {
1144             color = lcolors[l % numLColors];
1145             break;
1146           }
1147         }
1148         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1149       }
1150       PetscCall(VecRestoreArray(coordinates, &coords));
1151       PetscCall(PetscViewerFlush(viewer));
1152       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1153     }
1154     /* Plot cells */
1155     if (dim == 3 || !drawNumbers[1]) {
1156       for (e = eStart; e < eEnd; ++e) {
1157         const PetscInt *cone;
1158 
1159         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1160         color = colors[rank % numColors];
1161         for (l = 0; l < numLabels; ++l) {
1162           PetscInt val;
1163           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1164           if (val >= 0) {
1165             color = lcolors[l % numLColors];
1166             break;
1167           }
1168         }
1169         PetscCall(DMPlexGetCone(dm, e, &cone));
1170         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1171       }
1172     } else {
1173       DMPolytopeType ct;
1174 
1175       /* Drawing a 2D polygon */
1176       for (c = cStart; c < cEnd; ++c) {
1177         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1178         PetscCall(DMPlexGetCellType(dm, c, &ct));
1179         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1180           const PetscInt *cone;
1181           PetscInt        coneSize, e;
1182 
1183           PetscCall(DMPlexGetCone(dm, c, &cone));
1184           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1185           for (e = 0; e < coneSize; ++e) {
1186             const PetscInt *econe;
1187 
1188             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1189             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1190           }
1191         } else {
1192           PetscInt *closure = NULL;
1193           PetscInt  closureSize, Nv = 0, v;
1194 
1195           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1196           for (p = 0; p < closureSize * 2; p += 2) {
1197             const PetscInt point = closure[p];
1198 
1199             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1200           }
1201           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1202           for (v = 0; v <= Nv; ++v) {
1203             const PetscInt vertex = closure[v % Nv];
1204 
1205             if (v > 0) {
1206               if (plotEdges) {
1207                 const PetscInt *edge;
1208                 PetscInt        endpoints[2], ne;
1209 
1210                 endpoints[0] = closure[v - 1];
1211                 endpoints[1] = vertex;
1212                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1213                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1214                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1215                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1216               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1217             }
1218             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1219           }
1220           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1221           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1222         }
1223       }
1224     }
1225     for (c = cStart; c < cEnd; ++c) {
1226       double             ccoords[3] = {0.0, 0.0, 0.0};
1227       PetscBool          isLabeled  = PETSC_FALSE;
1228       PetscScalar       *cellCoords = NULL;
1229       const PetscScalar *array;
1230       PetscInt           numCoords, cdim, d;
1231       PetscBool          isDG;
1232 
1233       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1234       PetscCall(DMGetCoordinateDim(dm, &cdim));
1235       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1236       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1237       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1238       for (p = 0; p < numCoords / cdim; ++p) {
1239         for (d = 0; d < cdim; ++d) {
1240           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1241           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1242         }
1243         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1244         if (cdim == 3) {
1245           PetscReal tmp = tcoords[1];
1246           tcoords[1]    = tcoords[2];
1247           tcoords[2]    = -tmp;
1248         }
1249         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1250       }
1251       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1252       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1253       for (d = 0; d < cdim; ++d) {
1254         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1255         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1256       }
1257       if (drawHasse) color = colors[depth % numColors];
1258       else color = colors[rank % numColors];
1259       for (l = 0; l < numLabels; ++l) {
1260         PetscInt val;
1261         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1262         if (val >= 0) {
1263           color     = lcolors[l % numLColors];
1264           isLabeled = PETSC_TRUE;
1265           break;
1266         }
1267       }
1268       if (drawNumbers[dim]) {
1269         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1270       } else if (drawColors[dim]) {
1271         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1272       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1273     }
1274     if (drawHasse) {
1275       color = colors[depth % numColors];
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1277       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1279       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1281 
1282       color = colors[1 % numColors];
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1286       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1288 
1289       color = colors[0 % numColors];
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1295 
1296       for (p = pStart; p < pEnd; ++p) {
1297         const PetscInt *cone;
1298         PetscInt        coneSize, cp;
1299 
1300         PetscCall(DMPlexGetCone(dm, p, &cone));
1301         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1302         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1303       }
1304     }
1305     PetscCall(PetscViewerFlush(viewer));
1306     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1307     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1308     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1309     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1310     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1311     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1312     PetscCall(PetscFree3(names, colors, lcolors));
1313     PetscCall(PetscBTDestroy(&wp));
1314   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1315     Vec                    cown, acown;
1316     VecScatter             sct;
1317     ISLocalToGlobalMapping g2l;
1318     IS                     gid, acis;
1319     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1320     MPI_Group              ggroup, ngroup;
1321     PetscScalar           *array, nid;
1322     const PetscInt        *idxs;
1323     PetscInt              *idxs2, *start, *adjacency, *work;
1324     PetscInt64             lm[3], gm[3];
1325     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1326     PetscMPIInt            d1, d2, rank;
1327 
1328     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1329     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1330 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1331     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1332 #endif
1333     if (ncomm != MPI_COMM_NULL) {
1334       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1335       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1336       d1 = 0;
1337       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1338       nid = d2;
1339       PetscCallMPI(MPI_Group_free(&ggroup));
1340       PetscCallMPI(MPI_Group_free(&ngroup));
1341       PetscCallMPI(MPI_Comm_free(&ncomm));
1342     } else nid = 0.0;
1343 
1344     /* Get connectivity */
1345     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1346     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1347 
1348     /* filter overlapped local cells */
1349     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1350     PetscCall(ISGetIndices(gid, &idxs));
1351     PetscCall(ISGetLocalSize(gid, &cum));
1352     PetscCall(PetscMalloc1(cum, &idxs2));
1353     for (c = cStart, cum = 0; c < cEnd; c++) {
1354       if (idxs[c - cStart] < 0) continue;
1355       idxs2[cum++] = idxs[c - cStart];
1356     }
1357     PetscCall(ISRestoreIndices(gid, &idxs));
1358     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1359     PetscCall(ISDestroy(&gid));
1360     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1361 
1362     /* support for node-aware cell locality */
1363     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1364     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1365     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1366     PetscCall(VecGetArray(cown, &array));
1367     for (c = 0; c < numVertices; c++) array[c] = nid;
1368     PetscCall(VecRestoreArray(cown, &array));
1369     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1370     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1371     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1372     PetscCall(ISDestroy(&acis));
1373     PetscCall(VecScatterDestroy(&sct));
1374     PetscCall(VecDestroy(&cown));
1375 
1376     /* compute edgeCut */
1377     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1378     PetscCall(PetscMalloc1(cum, &work));
1379     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1380     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1381     PetscCall(ISDestroy(&gid));
1382     PetscCall(VecGetArray(acown, &array));
1383     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1384       PetscInt totl;
1385 
1386       totl = start[c + 1] - start[c];
1387       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1388       for (i = 0; i < totl; i++) {
1389         if (work[i] < 0) {
1390           ect += 1;
1391           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1392         }
1393       }
1394     }
1395     PetscCall(PetscFree(work));
1396     PetscCall(VecRestoreArray(acown, &array));
1397     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1398     lm[1] = -numVertices;
1399     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1400     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1401     lm[0] = ect;                     /* edgeCut */
1402     lm[1] = ectn;                    /* node-aware edgeCut */
1403     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1404     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1405     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1406 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1407     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1408 #else
1409     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1410 #endif
1411     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1412     PetscCall(PetscFree(start));
1413     PetscCall(PetscFree(adjacency));
1414     PetscCall(VecDestroy(&acown));
1415   } else {
1416     const char    *name;
1417     PetscInt      *sizes, *hybsizes, *ghostsizes;
1418     PetscInt       locDepth, depth, cellHeight, dim, d;
1419     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1420     PetscInt       numLabels, l, maxSize = 17;
1421     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1422     MPI_Comm       comm;
1423     PetscMPIInt    size, rank;
1424 
1425     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1426     PetscCallMPI(MPI_Comm_size(comm, &size));
1427     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1428     PetscCall(DMGetDimension(dm, &dim));
1429     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1430     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1431     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1432     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1433     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1434     PetscCall(DMPlexGetDepth(dm, &locDepth));
1435     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1436     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1437     gcNum = gcEnd - gcStart;
1438     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1439     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1440     for (d = 0; d <= depth; d++) {
1441       PetscInt Nc[2] = {0, 0}, ict;
1442 
1443       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1444       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1445       ict = ct0;
1446       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1447       ct0 = (DMPolytopeType)ict;
1448       for (p = pStart; p < pEnd; ++p) {
1449         DMPolytopeType ct;
1450 
1451         PetscCall(DMPlexGetCellType(dm, p, &ct));
1452         if (ct == ct0) ++Nc[0];
1453         else ++Nc[1];
1454       }
1455       if (size < maxSize) {
1456         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1457         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1458         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1459         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1460         for (p = 0; p < size; ++p) {
1461           if (rank == 0) {
1462             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1463             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1464             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1465           }
1466         }
1467       } else {
1468         PetscInt locMinMax[2];
1469 
1470         locMinMax[0] = Nc[0] + Nc[1];
1471         locMinMax[1] = Nc[0] + Nc[1];
1472         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1473         locMinMax[0] = Nc[1];
1474         locMinMax[1] = Nc[1];
1475         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1476         if (d == depth) {
1477           locMinMax[0] = gcNum;
1478           locMinMax[1] = gcNum;
1479           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1480         }
1481         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1482         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1483         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1484         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1485       }
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1487     }
1488     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1489     {
1490       const PetscReal *maxCell;
1491       const PetscReal *L;
1492       PetscBool        localized;
1493 
1494       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1495       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1496       if (L || localized) {
1497         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1498         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1499         if (L) {
1500           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1501           for (d = 0; d < dim; ++d) {
1502             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1503             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1504           }
1505           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1506         }
1507         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1508         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1509       }
1510     }
1511     PetscCall(DMGetNumLabels(dm, &numLabels));
1512     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1513     for (l = 0; l < numLabels; ++l) {
1514       DMLabel         label;
1515       const char     *name;
1516       IS              valueIS;
1517       const PetscInt *values;
1518       PetscInt        numValues, v;
1519 
1520       PetscCall(DMGetLabelName(dm, l, &name));
1521       PetscCall(DMGetLabel(dm, name, &label));
1522       PetscCall(DMLabelGetNumValues(label, &numValues));
1523       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1524       PetscCall(DMLabelGetValueIS(label, &valueIS));
1525       PetscCall(ISGetIndices(valueIS, &values));
1526       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1527       for (v = 0; v < numValues; ++v) {
1528         PetscInt size;
1529 
1530         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1531         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1532         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1533       }
1534       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1535       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1536       PetscCall(ISRestoreIndices(valueIS, &values));
1537       PetscCall(ISDestroy(&valueIS));
1538     }
1539     {
1540       char    **labelNames;
1541       PetscInt  Nl = numLabels;
1542       PetscBool flg;
1543 
1544       PetscCall(PetscMalloc1(Nl, &labelNames));
1545       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1546       for (l = 0; l < Nl; ++l) {
1547         DMLabel label;
1548 
1549         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1550         if (flg) {
1551           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1552           PetscCall(DMLabelView(label, viewer));
1553         }
1554         PetscCall(PetscFree(labelNames[l]));
1555       }
1556       PetscCall(PetscFree(labelNames));
1557     }
1558     /* If no fields are specified, people do not want to see adjacency */
1559     if (dm->Nf) {
1560       PetscInt f;
1561 
1562       for (f = 0; f < dm->Nf; ++f) {
1563         const char *name;
1564 
1565         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1566         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1567         PetscCall(PetscViewerASCIIPushTab(viewer));
1568         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1569         if (dm->fields[f].adjacency[0]) {
1570           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1571           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1572         } else {
1573           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1574           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1575         }
1576         PetscCall(PetscViewerASCIIPopTab(viewer));
1577       }
1578     }
1579     PetscCall(DMGetCoarseDM(dm, &cdm));
1580     if (cdm) {
1581       PetscCall(PetscViewerASCIIPushTab(viewer));
1582       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1583       PetscCall(DMPlexView_Ascii(cdm, viewer));
1584       PetscCall(PetscViewerASCIIPopTab(viewer));
1585     }
1586   }
1587   PetscFunctionReturn(PETSC_SUCCESS);
1588 }
1589 
1590 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1591 {
1592   DMPolytopeType ct;
1593   PetscMPIInt    rank;
1594   PetscInt       cdim;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   PetscCall(DMGetCoordinateDim(dm, &cdim));
1600   switch (ct) {
1601   case DM_POLYTOPE_SEGMENT:
1602   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1603     switch (cdim) {
1604     case 1: {
1605       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1606       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1607 
1608       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1609       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1610       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1611     } break;
1612     case 2: {
1613       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1614       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1615       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1616 
1617       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1618       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1619       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1620     } break;
1621     default:
1622       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1623     }
1624     break;
1625   case DM_POLYTOPE_TRIANGLE:
1626     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1627     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1628     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1629     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1630     break;
1631   case DM_POLYTOPE_QUADRILATERAL:
1632     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1633     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1635     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1636     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1637     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1638     break;
1639   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1640     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1641     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1642     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1643     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1644     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1645     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1646     break;
1647   case DM_POLYTOPE_FV_GHOST:
1648     break;
1649   default:
1650     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1651   }
1652   PetscFunctionReturn(PETSC_SUCCESS);
1653 }
1654 
1655 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1656 {
1657   DMPolytopeType ct;
1658   PetscReal      centroid[2] = {0., 0.};
1659   PetscMPIInt    rank;
1660   PetscInt       fillColor, v, e, d;
1661 
1662   PetscFunctionBegin;
1663   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1664   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1665   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1666   switch (ct) {
1667   case DM_POLYTOPE_TRIANGLE: {
1668     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1669 
1670     for (v = 0; v < 3; ++v) {
1671       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1672       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1673     }
1674     for (e = 0; e < 3; ++e) {
1675       refCoords[0] = refVertices[e * 2 + 0];
1676       refCoords[1] = refVertices[e * 2 + 1];
1677       for (d = 1; d <= edgeDiv; ++d) {
1678         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1679         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1680       }
1681       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1682       for (d = 0; d < edgeDiv; ++d) {
1683         PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1684         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1685       }
1686     }
1687   } break;
1688   default:
1689     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1690   }
1691   PetscFunctionReturn(PETSC_SUCCESS);
1692 }
1693 
1694 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1695 {
1696   PetscDraw    draw;
1697   DM           cdm;
1698   PetscSection coordSection;
1699   Vec          coordinates;
1700   PetscReal    xyl[3], xyr[3];
1701   PetscReal   *refCoords, *edgeCoords;
1702   PetscBool    isnull, drawAffine = PETSC_TRUE;
1703   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1704 
1705   PetscFunctionBegin;
1706   PetscCall(DMGetCoordinateDim(dm, &dim));
1707   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1708   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1709   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1710   PetscCall(DMGetCoordinateDM(dm, &cdm));
1711   PetscCall(DMGetLocalSection(cdm, &coordSection));
1712   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1713   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1714   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1715 
1716   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1717   PetscCall(PetscDrawIsNull(draw, &isnull));
1718   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1719   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1720 
1721   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1722   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1723   PetscCall(PetscDrawClear(draw));
1724 
1725   for (c = cStart; c < cEnd; ++c) {
1726     PetscScalar       *coords = NULL;
1727     const PetscScalar *coords_arr;
1728     PetscInt           numCoords;
1729     PetscBool          isDG;
1730 
1731     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1732     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1733     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1734     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1735   }
1736   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1737   PetscCall(PetscDrawFlush(draw));
1738   PetscCall(PetscDrawPause(draw));
1739   PetscCall(PetscDrawSave(draw));
1740   PetscFunctionReturn(PETSC_SUCCESS);
1741 }
1742 
1743 #if defined(PETSC_HAVE_EXODUSII)
1744   #include <exodusII.h>
1745   #include <petscviewerexodusii.h>
1746 #endif
1747 
1748 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1749 {
1750   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1751   char      name[PETSC_MAX_PATH_LEN];
1752 
1753   PetscFunctionBegin;
1754   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1755   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1756   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1757   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1759   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1761   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1762   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1763   if (iascii) {
1764     PetscViewerFormat format;
1765     PetscCall(PetscViewerGetFormat(viewer, &format));
1766     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1767     else PetscCall(DMPlexView_Ascii(dm, viewer));
1768   } else if (ishdf5) {
1769 #if defined(PETSC_HAVE_HDF5)
1770     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1771 #else
1772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1773 #endif
1774   } else if (isvtk) {
1775     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1776   } else if (isdraw) {
1777     PetscCall(DMPlexView_Draw(dm, viewer));
1778   } else if (isglvis) {
1779     PetscCall(DMPlexView_GLVis(dm, viewer));
1780 #if defined(PETSC_HAVE_EXODUSII)
1781   } else if (isexodus) {
1782     /*
1783       exodusII requires that all sets be part of exactly one cell set.
1784       If the dm does not have a "Cell Sets" label defined, we create one
1785       with ID 1, containing all cells.
1786       Note that if the Cell Sets label is defined but does not cover all cells,
1787       we may still have a problem. This should probably be checked here or in the viewer;
1788     */
1789     PetscInt numCS;
1790     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1791     if (!numCS) {
1792       PetscInt cStart, cEnd, c;
1793       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1794       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1795       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1796     }
1797     PetscCall(DMView_PlexExodusII(dm, viewer));
1798 #endif
1799 #if defined(PETSC_HAVE_CGNS)
1800   } else if (iscgns) {
1801     PetscCall(DMView_PlexCGNS(dm, viewer));
1802 #endif
1803   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1804   /* Optionally view the partition */
1805   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1806   if (flg) {
1807     Vec ranks;
1808     PetscCall(DMPlexCreateRankField(dm, &ranks));
1809     PetscCall(VecView(ranks, viewer));
1810     PetscCall(VecDestroy(&ranks));
1811   }
1812   /* Optionally view a label */
1813   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1814   if (flg) {
1815     DMLabel label;
1816     Vec     val;
1817 
1818     PetscCall(DMGetLabel(dm, name, &label));
1819     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1820     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1821     PetscCall(VecView(val, viewer));
1822     PetscCall(VecDestroy(&val));
1823   }
1824   PetscFunctionReturn(PETSC_SUCCESS);
1825 }
1826 
1827 /*@
1828   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1829 
1830   Collective
1831 
1832   Input Parameters:
1833 + dm     - The `DM` whose topology is to be saved
1834 - viewer - The `PetscViewer` to save it in
1835 
1836   Level: advanced
1837 
1838 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1839 @*/
1840 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1841 {
1842   PetscBool ishdf5;
1843 
1844   PetscFunctionBegin;
1845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1846   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1847   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1848   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1849   if (ishdf5) {
1850 #if defined(PETSC_HAVE_HDF5)
1851     PetscViewerFormat format;
1852     PetscCall(PetscViewerGetFormat(viewer, &format));
1853     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1854       IS globalPointNumbering;
1855 
1856       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1857       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1858       PetscCall(ISDestroy(&globalPointNumbering));
1859     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1860 #else
1861     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1862 #endif
1863   }
1864   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 /*@
1869   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1870 
1871   Collective
1872 
1873   Input Parameters:
1874 + dm     - The `DM` whose coordinates are to be saved
1875 - viewer - The `PetscViewer` for saving
1876 
1877   Level: advanced
1878 
1879 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1880 @*/
1881 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1882 {
1883   PetscBool ishdf5;
1884 
1885   PetscFunctionBegin;
1886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1887   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1888   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1889   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1890   if (ishdf5) {
1891 #if defined(PETSC_HAVE_HDF5)
1892     PetscViewerFormat format;
1893     PetscCall(PetscViewerGetFormat(viewer, &format));
1894     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1895       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1896     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1897 #else
1898     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1899 #endif
1900   }
1901   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1902   PetscFunctionReturn(PETSC_SUCCESS);
1903 }
1904 
1905 /*@
1906   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1907 
1908   Collective
1909 
1910   Input Parameters:
1911 + dm     - The `DM` whose labels are to be saved
1912 - viewer - The `PetscViewer` for saving
1913 
1914   Level: advanced
1915 
1916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1917 @*/
1918 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1919 {
1920   PetscBool ishdf5;
1921 
1922   PetscFunctionBegin;
1923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1924   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1925   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1926   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1927   if (ishdf5) {
1928 #if defined(PETSC_HAVE_HDF5)
1929     IS                globalPointNumbering;
1930     PetscViewerFormat format;
1931 
1932     PetscCall(PetscViewerGetFormat(viewer, &format));
1933     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1934       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1935       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1936       PetscCall(ISDestroy(&globalPointNumbering));
1937     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1938 #else
1939     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1940 #endif
1941   }
1942   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1943   PetscFunctionReturn(PETSC_SUCCESS);
1944 }
1945 
1946 /*@
1947   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1948 
1949   Collective
1950 
1951   Input Parameters:
1952 + dm         - The `DM` that contains the topology on which the section to be saved is defined
1953 . viewer     - The `PetscViewer` for saving
1954 - sectiondm  - The `DM` that contains the section to be saved
1955 
1956   Level: advanced
1957 
1958   Notes:
1959   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
1960 
1961   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1962 
1963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1964 @*/
1965 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1966 {
1967   PetscBool ishdf5;
1968 
1969   PetscFunctionBegin;
1970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1971   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1972   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1973   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1974   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1975   if (ishdf5) {
1976 #if defined(PETSC_HAVE_HDF5)
1977     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1978 #else
1979     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1980 #endif
1981   }
1982   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1983   PetscFunctionReturn(PETSC_SUCCESS);
1984 }
1985 
1986 /*@
1987   DMPlexGlobalVectorView - Saves a global vector
1988 
1989   Collective
1990 
1991   Input Parameters:
1992 + dm        - The `DM` that represents the topology
1993 . viewer    - The `PetscViewer` to save data with
1994 . sectiondm - The `DM` that contains the global section on which vec is defined
1995 - vec       - The global vector to be saved
1996 
1997   Level: advanced
1998 
1999   Notes:
2000   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2001 
2002   Typical calling sequence:
2003 .vb
2004        DMCreate(PETSC_COMM_WORLD, &dm);
2005        DMSetType(dm, DMPLEX);
2006        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2007        DMClone(dm, &sectiondm);
2008        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2009        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2010        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2011        PetscSectionSetChart(section, pStart, pEnd);
2012        PetscSectionSetUp(section);
2013        DMSetLocalSection(sectiondm, section);
2014        PetscSectionDestroy(&section);
2015        DMGetGlobalVector(sectiondm, &vec);
2016        PetscObjectSetName((PetscObject)vec, "vec_name");
2017        DMPlexTopologyView(dm, viewer);
2018        DMPlexSectionView(dm, viewer, sectiondm);
2019        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2020        DMRestoreGlobalVector(sectiondm, &vec);
2021        DMDestroy(&sectiondm);
2022        DMDestroy(&dm);
2023 .ve
2024 
2025 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2026 @*/
2027 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2028 {
2029   PetscBool ishdf5;
2030 
2031   PetscFunctionBegin;
2032   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2033   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2034   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2035   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2036   /* Check consistency */
2037   {
2038     PetscSection section;
2039     PetscBool    includesConstraints;
2040     PetscInt     m, m1;
2041 
2042     PetscCall(VecGetLocalSize(vec, &m1));
2043     PetscCall(DMGetGlobalSection(sectiondm, &section));
2044     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2045     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2046     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2047     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2048   }
2049   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2050   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2051   if (ishdf5) {
2052 #if defined(PETSC_HAVE_HDF5)
2053     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2054 #else
2055     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2056 #endif
2057   }
2058   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2059   PetscFunctionReturn(PETSC_SUCCESS);
2060 }
2061 
2062 /*@
2063   DMPlexLocalVectorView - Saves a local vector
2064 
2065   Collective
2066 
2067   Input Parameters:
2068 + dm        - The `DM` that represents the topology
2069 . viewer    - The `PetscViewer` to save data with
2070 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2071 - vec       - The local vector to be saved
2072 
2073   Level: advanced
2074 
2075   Note:
2076   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2077 
2078   Typical calling sequence:
2079 .vb
2080        DMCreate(PETSC_COMM_WORLD, &dm);
2081        DMSetType(dm, DMPLEX);
2082        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2083        DMClone(dm, &sectiondm);
2084        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2085        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2086        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2087        PetscSectionSetChart(section, pStart, pEnd);
2088        PetscSectionSetUp(section);
2089        DMSetLocalSection(sectiondm, section);
2090        DMGetLocalVector(sectiondm, &vec);
2091        PetscObjectSetName((PetscObject)vec, "vec_name");
2092        DMPlexTopologyView(dm, viewer);
2093        DMPlexSectionView(dm, viewer, sectiondm);
2094        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2095        DMRestoreLocalVector(sectiondm, &vec);
2096        DMDestroy(&sectiondm);
2097        DMDestroy(&dm);
2098 .ve
2099 
2100 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2101 @*/
2102 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2103 {
2104   PetscBool ishdf5;
2105 
2106   PetscFunctionBegin;
2107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2108   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2109   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2110   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2111   /* Check consistency */
2112   {
2113     PetscSection section;
2114     PetscBool    includesConstraints;
2115     PetscInt     m, m1;
2116 
2117     PetscCall(VecGetLocalSize(vec, &m1));
2118     PetscCall(DMGetLocalSection(sectiondm, &section));
2119     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2120     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2121     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2122     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2123   }
2124   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2125   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2126   if (ishdf5) {
2127 #if defined(PETSC_HAVE_HDF5)
2128     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2129 #else
2130     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2131 #endif
2132   }
2133   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2134   PetscFunctionReturn(PETSC_SUCCESS);
2135 }
2136 
2137 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2138 {
2139   PetscBool ishdf5;
2140 
2141   PetscFunctionBegin;
2142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2143   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2144   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2145   if (ishdf5) {
2146 #if defined(PETSC_HAVE_HDF5)
2147     PetscViewerFormat format;
2148     PetscCall(PetscViewerGetFormat(viewer, &format));
2149     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2150       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2151     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2152       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2153     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2154     PetscFunctionReturn(PETSC_SUCCESS);
2155 #else
2156     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2157 #endif
2158   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2159 }
2160 
2161 /*@
2162   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2163 
2164   Collective
2165 
2166   Input Parameters:
2167 + dm                - The `DM` into which the topology is loaded
2168 - viewer            - The `PetscViewer` for the saved topology
2169 
2170   Output Parameter:
2171 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2172 
2173   Level: advanced
2174 
2175 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2176           `PetscViewer`, `PetscSF`
2177 @*/
2178 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2179 {
2180   PetscBool ishdf5;
2181 
2182   PetscFunctionBegin;
2183   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2184   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2185   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2186   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2187   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2188   if (ishdf5) {
2189 #if defined(PETSC_HAVE_HDF5)
2190     PetscViewerFormat format;
2191     PetscCall(PetscViewerGetFormat(viewer, &format));
2192     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2193       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2194     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2195 #else
2196     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2197 #endif
2198   }
2199   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2200   PetscFunctionReturn(PETSC_SUCCESS);
2201 }
2202 
2203 /*@
2204   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2205 
2206   Collective
2207 
2208   Input Parameters:
2209 + dm     - The `DM` into which the coordinates are loaded
2210 . viewer - The `PetscViewer` for the saved coordinates
2211 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2212 
2213   Level: advanced
2214 
2215 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2216           `PetscSF`, `PetscViewer`
2217 @*/
2218 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2219 {
2220   PetscBool ishdf5;
2221 
2222   PetscFunctionBegin;
2223   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2224   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2225   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2226   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2227   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2228   if (ishdf5) {
2229 #if defined(PETSC_HAVE_HDF5)
2230     PetscViewerFormat format;
2231     PetscCall(PetscViewerGetFormat(viewer, &format));
2232     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2233       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2234     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2235 #else
2236     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2237 #endif
2238   }
2239   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2240   PetscFunctionReturn(PETSC_SUCCESS);
2241 }
2242 
2243 /*@
2244   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2245 
2246   Collective
2247 
2248   Input Parameters:
2249 + dm     - The `DM` into which the labels are loaded
2250 . viewer - The `PetscViewer` for the saved labels
2251 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2252 
2253   Level: advanced
2254 
2255   Note:
2256   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2257 
2258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2259           `PetscSF`, `PetscViewer`
2260 @*/
2261 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2262 {
2263   PetscBool ishdf5;
2264 
2265   PetscFunctionBegin;
2266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2267   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2268   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2269   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2270   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2271   if (ishdf5) {
2272 #if defined(PETSC_HAVE_HDF5)
2273     PetscViewerFormat format;
2274 
2275     PetscCall(PetscViewerGetFormat(viewer, &format));
2276     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2277       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2278     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2279 #else
2280     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2281 #endif
2282   }
2283   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2284   PetscFunctionReturn(PETSC_SUCCESS);
2285 }
2286 
2287 /*@
2288   DMPlexSectionLoad - Loads section into a `DMPLEX`
2289 
2290   Collective
2291 
2292   Input Parameters:
2293 + dm          - The `DM` that represents the topology
2294 . viewer      - The `PetscViewer` that represents the on-disk section (sectionA)
2295 . sectiondm   - The `DM` into which the on-disk section (sectionA) is migrated
2296 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2297 
2298   Output Parameters
2299 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2300 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2301 
2302   Level: advanced
2303 
2304   Notes:
2305   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2306 
2307   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2308 
2309   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2310 
2311   Example using 2 processes:
2312 .vb
2313   NX (number of points on dm): 4
2314   sectionA                   : the on-disk section
2315   vecA                       : a vector associated with sectionA
2316   sectionB                   : sectiondm's local section constructed in this function
2317   vecB (local)               : a vector associated with sectiondm's local section
2318   vecB (global)              : a vector associated with sectiondm's global section
2319 
2320                                      rank 0    rank 1
2321   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2322   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2323   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2324   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2325   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2326   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2327   sectionB->atlasDof             :     1 0 1 | 1 3
2328   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2329   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2330   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2331 .ve
2332   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2333 
2334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2335 @*/
2336 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2344   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2345   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2346   if (localDofSF) PetscValidPointer(localDofSF, 6);
2347   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2348   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2349   if (ishdf5) {
2350 #if defined(PETSC_HAVE_HDF5)
2351     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2352 #else
2353     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2354 #endif
2355   }
2356   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2357   PetscFunctionReturn(PETSC_SUCCESS);
2358 }
2359 
2360 /*@
2361   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2362 
2363   Collective
2364 
2365   Input Parameters:
2366 + dm        - The `DM` that represents the topology
2367 . viewer    - The `PetscViewer` that represents the on-disk vector data
2368 . sectiondm - The `DM` that contains the global section on which vec is defined
2369 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2370 - vec       - The global vector to set values of
2371 
2372   Level: advanced
2373 
2374   Notes:
2375   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2376 
2377   Typical calling sequence:
2378 .vb
2379        DMCreate(PETSC_COMM_WORLD, &dm);
2380        DMSetType(dm, DMPLEX);
2381        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2382        DMPlexTopologyLoad(dm, viewer, &sfX);
2383        DMClone(dm, &sectiondm);
2384        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2385        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2386        DMGetGlobalVector(sectiondm, &vec);
2387        PetscObjectSetName((PetscObject)vec, "vec_name");
2388        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2389        DMRestoreGlobalVector(sectiondm, &vec);
2390        PetscSFDestroy(&gsf);
2391        PetscSFDestroy(&sfX);
2392        DMDestroy(&sectiondm);
2393        DMDestroy(&dm);
2394 .ve
2395 
2396 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2397           `PetscSF`, `PetscViewer`
2398 @*/
2399 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2400 {
2401   PetscBool ishdf5;
2402 
2403   PetscFunctionBegin;
2404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2405   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2406   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2407   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2408   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2409   /* Check consistency */
2410   {
2411     PetscSection section;
2412     PetscBool    includesConstraints;
2413     PetscInt     m, m1;
2414 
2415     PetscCall(VecGetLocalSize(vec, &m1));
2416     PetscCall(DMGetGlobalSection(sectiondm, &section));
2417     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2418     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2419     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2420     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2421   }
2422   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2423   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2424   if (ishdf5) {
2425 #if defined(PETSC_HAVE_HDF5)
2426     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2427 #else
2428     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2429 #endif
2430   }
2431   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2432   PetscFunctionReturn(PETSC_SUCCESS);
2433 }
2434 
2435 /*@
2436   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2437 
2438   Collective
2439 
2440   Input Parameters:
2441 + dm        - The `DM` that represents the topology
2442 . viewer    - The `PetscViewer` that represents the on-disk vector data
2443 . sectiondm - The `DM` that contains the local section on which vec is defined
2444 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2445 - vec       - The local vector to set values of
2446 
2447   Level: advanced
2448 
2449   Notes:
2450   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2451 
2452   Typical calling sequence:
2453 .vb
2454        DMCreate(PETSC_COMM_WORLD, &dm);
2455        DMSetType(dm, DMPLEX);
2456        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2457        DMPlexTopologyLoad(dm, viewer, &sfX);
2458        DMClone(dm, &sectiondm);
2459        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2460        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2461        DMGetLocalVector(sectiondm, &vec);
2462        PetscObjectSetName((PetscObject)vec, "vec_name");
2463        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2464        DMRestoreLocalVector(sectiondm, &vec);
2465        PetscSFDestroy(&lsf);
2466        PetscSFDestroy(&sfX);
2467        DMDestroy(&sectiondm);
2468        DMDestroy(&dm);
2469 .ve
2470 
2471 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2472           `PetscSF`, `PetscViewer`
2473 @*/
2474 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2475 {
2476   PetscBool ishdf5;
2477 
2478   PetscFunctionBegin;
2479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2480   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2481   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2482   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2483   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2484   /* Check consistency */
2485   {
2486     PetscSection section;
2487     PetscBool    includesConstraints;
2488     PetscInt     m, m1;
2489 
2490     PetscCall(VecGetLocalSize(vec, &m1));
2491     PetscCall(DMGetLocalSection(sectiondm, &section));
2492     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2493     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2494     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2495     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2496   }
2497   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2498   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2499   if (ishdf5) {
2500 #if defined(PETSC_HAVE_HDF5)
2501     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2502 #else
2503     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2504 #endif
2505   }
2506   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2507   PetscFunctionReturn(PETSC_SUCCESS);
2508 }
2509 
2510 PetscErrorCode DMDestroy_Plex(DM dm)
2511 {
2512   DM_Plex *mesh = (DM_Plex *)dm->data;
2513 
2514   PetscFunctionBegin;
2515   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2516   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2517   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2518   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2519   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2520   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2521   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2522   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2523   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2524   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2525   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2526   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2527   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2528   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2529   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2530   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2531   PetscCall(PetscFree(mesh->cones));
2532   PetscCall(PetscFree(mesh->coneOrientations));
2533   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2534   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2535   PetscCall(PetscFree(mesh->supports));
2536   PetscCall(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 . mesh - The `DMPLEX`
2687 
2688   Output Parameter:
2689 . subsection - The subdomain section
2690 
2691   Level: developer
2692 
2693 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2694 @*/
2695 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2696 {
2697   DM_Plex *mesh = (DM_Plex *)dm->data;
2698 
2699   PetscFunctionBegin;
2700   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2701   if (!mesh->subdomainSection) {
2702     PetscSection section;
2703     PetscSF      sf;
2704 
2705     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2706     PetscCall(DMGetLocalSection(dm, &section));
2707     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2708     PetscCall(PetscSFDestroy(&sf));
2709   }
2710   *subsection = mesh->subdomainSection;
2711   PetscFunctionReturn(PETSC_SUCCESS);
2712 }
2713 
2714 /*@
2715   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2716 
2717   Not Collective
2718 
2719   Input Parameter:
2720 . mesh - The `DMPLEX`
2721 
2722   Output Parameters:
2723 + pStart - The first mesh point
2724 - pEnd   - The upper bound for mesh points
2725 
2726   Level: beginner
2727 
2728 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2729 @*/
2730 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2731 {
2732   DM_Plex *mesh = (DM_Plex *)dm->data;
2733 
2734   PetscFunctionBegin;
2735   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2736   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2737   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2738   PetscFunctionReturn(PETSC_SUCCESS);
2739 }
2740 
2741 /*@
2742   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2743 
2744   Not Collective
2745 
2746   Input Parameters:
2747 + mesh - The `DMPLEX`
2748 . pStart - The first mesh point
2749 - pEnd   - The upper bound for mesh points
2750 
2751   Level: beginner
2752 
2753 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2754 @*/
2755 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2756 {
2757   DM_Plex *mesh = (DM_Plex *)dm->data;
2758   PetscInt pStartO, pEndO;
2759 
2760   PetscFunctionBegin;
2761   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2762   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStartO, &pEndO));
2763   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2764   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2765   PetscCall(PetscFree(mesh->cellTypes));
2766   PetscFunctionReturn(PETSC_SUCCESS);
2767 }
2768 
2769 /*@
2770   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2771 
2772   Not Collective
2773 
2774   Input Parameters:
2775 + mesh - The `DMPLEX`
2776 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2777 
2778   Output Parameter:
2779 . size - The cone size for point `p`
2780 
2781   Level: beginner
2782 
2783 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2784 @*/
2785 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2786 {
2787   DM_Plex *mesh = (DM_Plex *)dm->data;
2788 
2789   PetscFunctionBegin;
2790   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2791   PetscValidIntPointer(size, 3);
2792   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2793   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2794   PetscFunctionReturn(PETSC_SUCCESS);
2795 }
2796 
2797 /*@
2798   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2799 
2800   Not Collective
2801 
2802   Input Parameters:
2803 + mesh - The `DMPLEX`
2804 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2805 - size - The cone size for point `p`
2806 
2807   Level: beginner
2808 
2809   Note:
2810   This should be called after `DMPlexSetChart()`.
2811 
2812 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2813 @*/
2814 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2815 {
2816   DM_Plex *mesh = (DM_Plex *)dm->data;
2817 
2818   PetscFunctionBegin;
2819   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2820   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2821   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2822   PetscFunctionReturn(PETSC_SUCCESS);
2823 }
2824 
2825 /*@C
2826   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2827 
2828   Not Collective
2829 
2830   Input Parameters:
2831 + dm - The `DMPLEX`
2832 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2833 
2834   Output Parameter:
2835 . cone - An array of points which are on the in-edges for point `p`
2836 
2837   Level: beginner
2838 
2839   Fortran Note:
2840   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2841   `DMPlexRestoreCone()` is not needed/available in C.
2842 
2843 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2844 @*/
2845 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2846 {
2847   DM_Plex *mesh = (DM_Plex *)dm->data;
2848   PetscInt off;
2849 
2850   PetscFunctionBegin;
2851   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2852   PetscValidPointer(cone, 3);
2853   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2854   *cone = &mesh->cones[off];
2855   PetscFunctionReturn(PETSC_SUCCESS);
2856 }
2857 
2858 /*@C
2859   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2860 
2861   Not Collective
2862 
2863   Input Parameters:
2864 + dm - The `DMPLEX`
2865 - p - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2866 
2867   Output Parameters:
2868 + pConesSection - `PetscSection` describing the layout of `pCones`
2869 - pCones - An array of points which are on the in-edges for the point set `p`
2870 
2871   Level: intermediate
2872 
2873 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2874 @*/
2875 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2876 {
2877   PetscSection cs, newcs;
2878   PetscInt    *cones;
2879   PetscInt    *newarr = NULL;
2880   PetscInt     n;
2881 
2882   PetscFunctionBegin;
2883   PetscCall(DMPlexGetCones(dm, &cones));
2884   PetscCall(DMPlexGetConeSection(dm, &cs));
2885   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2886   if (pConesSection) *pConesSection = newcs;
2887   if (pCones) {
2888     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2889     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2890   }
2891   PetscFunctionReturn(PETSC_SUCCESS);
2892 }
2893 
2894 /*@
2895   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2896 
2897   Not Collective
2898 
2899   Input Parameters:
2900 + dm - The `DMPLEX`
2901 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2902 
2903   Output Parameter:
2904 . expandedPoints - An array of vertices recursively expanded from input points
2905 
2906   Level: advanced
2907 
2908   Notes:
2909   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
2910 
2911   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2912 
2913 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2914           `DMPlexGetDepth()`, `IS`
2915 @*/
2916 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2917 {
2918   IS      *expandedPointsAll;
2919   PetscInt depth;
2920 
2921   PetscFunctionBegin;
2922   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2923   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2924   PetscValidPointer(expandedPoints, 3);
2925   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2926   *expandedPoints = expandedPointsAll[0];
2927   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2928   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2929   PetscFunctionReturn(PETSC_SUCCESS);
2930 }
2931 
2932 /*@
2933   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).
2934 
2935   Not Collective
2936 
2937   Input Parameters:
2938 + dm - The `DMPLEX`
2939 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2940 
2941   Output Parameters:
2942 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2943 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2944 - sections - (optional) An array of sections which describe mappings from points to their cone points
2945 
2946   Level: advanced
2947 
2948   Notes:
2949   Like `DMPlexGetConeTuple()` but recursive.
2950 
2951   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.
2952   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2953 
2954   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:
2955   (1) DAG points in expandedPoints[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
2956   (2) DAG points in expandedPoints[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
2957 
2958 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2959           `DMPlexGetDepth()`, `PetscSection`, `IS`
2960 @*/
2961 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2962 {
2963   const PetscInt *arr0 = NULL, *cone = NULL;
2964   PetscInt       *arr = NULL, *newarr = NULL;
2965   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2966   IS             *expandedPoints_;
2967   PetscSection   *sections_;
2968 
2969   PetscFunctionBegin;
2970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2971   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2972   if (depth) PetscValidIntPointer(depth, 3);
2973   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2974   if (sections) PetscValidPointer(sections, 5);
2975   PetscCall(ISGetLocalSize(points, &n));
2976   PetscCall(ISGetIndices(points, &arr0));
2977   PetscCall(DMPlexGetDepth(dm, &depth_));
2978   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2979   PetscCall(PetscCalloc1(depth_, &sections_));
2980   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2981   for (d = depth_ - 1; d >= 0; d--) {
2982     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2983     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2984     for (i = 0; i < n; i++) {
2985       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2986       if (arr[i] >= start && arr[i] < end) {
2987         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2988         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2989       } else {
2990         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2991       }
2992     }
2993     PetscCall(PetscSectionSetUp(sections_[d]));
2994     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2995     PetscCall(PetscMalloc1(newn, &newarr));
2996     for (i = 0; i < n; i++) {
2997       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2998       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2999       if (cn > 1) {
3000         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3001         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3002       } else {
3003         newarr[co] = arr[i];
3004       }
3005     }
3006     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3007     arr = newarr;
3008     n   = newn;
3009   }
3010   PetscCall(ISRestoreIndices(points, &arr0));
3011   *depth = depth_;
3012   if (expandedPoints) *expandedPoints = expandedPoints_;
3013   else {
3014     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3015     PetscCall(PetscFree(expandedPoints_));
3016   }
3017   if (sections) *sections = sections_;
3018   else {
3019     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3020     PetscCall(PetscFree(sections_));
3021   }
3022   PetscFunctionReturn(PETSC_SUCCESS);
3023 }
3024 
3025 /*@
3026   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3027 
3028   Not Collective
3029 
3030   Input Parameters:
3031 + dm - The `DMPLEX`
3032 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3033 
3034   Output Parameters:
3035 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3036 . expandedPoints - (optional) An array of recursively expanded cones
3037 - sections - (optional) An array of sections which describe mappings from points to their cone points
3038 
3039   Level: advanced
3040 
3041   Note:
3042   See `DMPlexGetConeRecursive()`
3043 
3044 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3045           `DMPlexGetDepth()`, `IS`, `PetscSection`
3046 @*/
3047 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3048 {
3049   PetscInt d, depth_;
3050 
3051   PetscFunctionBegin;
3052   PetscCall(DMPlexGetDepth(dm, &depth_));
3053   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3054   if (depth) *depth = 0;
3055   if (expandedPoints) {
3056     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3057     PetscCall(PetscFree(*expandedPoints));
3058   }
3059   if (sections) {
3060     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3061     PetscCall(PetscFree(*sections));
3062   }
3063   PetscFunctionReturn(PETSC_SUCCESS);
3064 }
3065 
3066 /*@
3067   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
3068 
3069   Not Collective
3070 
3071   Input Parameters:
3072 + mesh - The `DMPLEX`
3073 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3074 - cone - An array of points which are on the in-edges for point `p`
3075 
3076   Level: beginner
3077 
3078   Note:
3079   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3080 
3081 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3082 @*/
3083 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3084 {
3085   DM_Plex *mesh = (DM_Plex *)dm->data;
3086   PetscInt dof, off, c;
3087 
3088   PetscFunctionBegin;
3089   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3090   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3091   if (dof) PetscValidIntPointer(cone, 3);
3092   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3093   if (PetscDefined(USE_DEBUG)) {
3094     PetscInt pStart, pEnd;
3095     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3096     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);
3097     for (c = 0; c < dof; ++c) {
3098       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);
3099       mesh->cones[off + c] = cone[c];
3100     }
3101   } else {
3102     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3103   }
3104   PetscFunctionReturn(PETSC_SUCCESS);
3105 }
3106 
3107 /*@C
3108   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3109 
3110   Not Collective
3111 
3112   Input Parameters:
3113 + mesh - The `DMPLEX`
3114 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3115 
3116   Output Parameter:
3117 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3118                     integer giving the prescription for cone traversal.
3119 
3120   Level: beginner
3121 
3122   Note:
3123   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3124   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3125   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3126   with the identity.
3127 
3128   Fortran Note:
3129   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3130   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3131 
3132 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3133 @*/
3134 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3135 {
3136   DM_Plex *mesh = (DM_Plex *)dm->data;
3137   PetscInt off;
3138 
3139   PetscFunctionBegin;
3140   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3141   if (PetscDefined(USE_DEBUG)) {
3142     PetscInt dof;
3143     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3144     if (dof) PetscValidPointer(coneOrientation, 3);
3145   }
3146   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3147 
3148   *coneOrientation = &mesh->coneOrientations[off];
3149   PetscFunctionReturn(PETSC_SUCCESS);
3150 }
3151 
3152 /*@
3153   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3154 
3155   Not Collective
3156 
3157   Input Parameters:
3158 + mesh - The `DMPLEX`
3159 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3160 - coneOrientation - An array of orientations
3161 
3162   Level: beginner
3163 
3164   Notes:
3165   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3166 
3167   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3168 
3169 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3170 @*/
3171 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3172 {
3173   DM_Plex *mesh = (DM_Plex *)dm->data;
3174   PetscInt pStart, pEnd;
3175   PetscInt dof, off, c;
3176 
3177   PetscFunctionBegin;
3178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3179   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3180   if (dof) PetscValidIntPointer(coneOrientation, 3);
3181   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3182   if (PetscDefined(USE_DEBUG)) {
3183     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3184     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);
3185     for (c = 0; c < dof; ++c) {
3186       PetscInt cdof, o = coneOrientation[c];
3187 
3188       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3189       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);
3190       mesh->coneOrientations[off + c] = o;
3191     }
3192   } else {
3193     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3194   }
3195   PetscFunctionReturn(PETSC_SUCCESS);
3196 }
3197 
3198 /*@
3199   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3200 
3201   Not Collective
3202 
3203   Input Parameters:
3204 + mesh - The `DMPLEX`
3205 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3206 . conePos - The local index in the cone where the point should be put
3207 - conePoint - The mesh point to insert
3208 
3209   Level: beginner
3210 
3211 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3212 @*/
3213 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3214 {
3215   DM_Plex *mesh = (DM_Plex *)dm->data;
3216   PetscInt pStart, pEnd;
3217   PetscInt dof, off;
3218 
3219   PetscFunctionBegin;
3220   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3221   if (PetscDefined(USE_DEBUG)) {
3222     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3223     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);
3224     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);
3225     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3226     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);
3227   }
3228   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3229   mesh->cones[off + conePos] = conePoint;
3230   PetscFunctionReturn(PETSC_SUCCESS);
3231 }
3232 
3233 /*@
3234   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3235 
3236   Not Collective
3237 
3238   Input Parameters:
3239 + mesh - The `DMPLEX`
3240 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3241 . conePos - The local index in the cone where the point should be put
3242 - coneOrientation - The point orientation to insert
3243 
3244   Level: beginner
3245 
3246   Note:
3247   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3248 
3249 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3250 @*/
3251 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3252 {
3253   DM_Plex *mesh = (DM_Plex *)dm->data;
3254   PetscInt pStart, pEnd;
3255   PetscInt dof, off;
3256 
3257   PetscFunctionBegin;
3258   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3259   if (PetscDefined(USE_DEBUG)) {
3260     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3261     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);
3262     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3263     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);
3264   }
3265   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3266   mesh->coneOrientations[off + conePos] = coneOrientation;
3267   PetscFunctionReturn(PETSC_SUCCESS);
3268 }
3269 
3270 /*@C
3271   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3272 
3273   Not collective
3274 
3275   Input Parameters:
3276 + dm - The DMPlex
3277 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3278 
3279   Output Parameters:
3280 + cone - An array of points which are on the in-edges for point `p`
3281 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3282         integer giving the prescription for cone traversal.
3283 
3284   Level: beginner
3285 
3286   Notes:
3287   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3288   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3289   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3290   with the identity.
3291 
3292   Fortran Notes:
3293   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3294   `DMPlexRestoreCone()` is not needed/available in C.
3295 
3296 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3297 @*/
3298 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3299 {
3300   DM_Plex *mesh = (DM_Plex *)dm->data;
3301 
3302   PetscFunctionBegin;
3303   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3304   if (mesh->tr) {
3305     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3306   } else {
3307     PetscInt off;
3308     if (PetscDefined(USE_DEBUG)) {
3309       PetscInt dof;
3310       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3311       if (dof) {
3312         if (cone) PetscValidPointer(cone, 3);
3313         if (ornt) PetscValidPointer(ornt, 4);
3314       }
3315     }
3316     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3317     if (cone) *cone = &mesh->cones[off];
3318     if (ornt) *ornt = &mesh->coneOrientations[off];
3319   }
3320   PetscFunctionReturn(PETSC_SUCCESS);
3321 }
3322 
3323 /*@C
3324   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3325 
3326   Not Collective
3327 
3328   Input Parameters:
3329 + dm - The DMPlex
3330 . p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3331 . cone - An array of points which are on the in-edges for point p
3332 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3333         integer giving the prescription for cone traversal.
3334 
3335   Level: beginner
3336 
3337   Notes:
3338   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3339   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3340   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3341   with the identity.
3342 
3343   Fortran Note:
3344   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3345   `DMPlexRestoreCone()` is not needed/available in C.
3346 
3347 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3348 @*/
3349 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3350 {
3351   DM_Plex *mesh = (DM_Plex *)dm->data;
3352 
3353   PetscFunctionBegin;
3354   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3355   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3356   PetscFunctionReturn(PETSC_SUCCESS);
3357 }
3358 
3359 /*@
3360   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3361 
3362   Not Collective
3363 
3364   Input Parameters:
3365 + mesh - The `DMPLEX`
3366 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3367 
3368   Output Parameter:
3369 . size - The support size for point `p`
3370 
3371   Level: beginner
3372 
3373 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3374 @*/
3375 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3376 {
3377   DM_Plex *mesh = (DM_Plex *)dm->data;
3378 
3379   PetscFunctionBegin;
3380   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3381   PetscValidIntPointer(size, 3);
3382   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3383   PetscFunctionReturn(PETSC_SUCCESS);
3384 }
3385 
3386 /*@
3387   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3388 
3389   Not Collective
3390 
3391   Input Parameters:
3392 + mesh - The `DMPLEX`
3393 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3394 - size - The support size for point `p`
3395 
3396   Level: beginner
3397 
3398   Note:
3399   This should be called after `DMPlexSetChart()`.
3400 
3401 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3402 @*/
3403 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3404 {
3405   DM_Plex *mesh = (DM_Plex *)dm->data;
3406 
3407   PetscFunctionBegin;
3408   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3409   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3410   PetscFunctionReturn(PETSC_SUCCESS);
3411 }
3412 
3413 /*@C
3414   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3415 
3416   Not Collective
3417 
3418   Input Parameters:
3419 + mesh - The `DMPLEX`
3420 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3421 
3422   Output Parameter:
3423 . support - An array of points which are on the out-edges for point `p`
3424 
3425   Level: beginner
3426 
3427   Fortran Note:
3428   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3429   `DMPlexRestoreSupport()` is not needed/available in C.
3430 
3431 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3432 @*/
3433 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3434 {
3435   DM_Plex *mesh = (DM_Plex *)dm->data;
3436   PetscInt off;
3437 
3438   PetscFunctionBegin;
3439   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3440   PetscValidPointer(support, 3);
3441   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3442   *support = &mesh->supports[off];
3443   PetscFunctionReturn(PETSC_SUCCESS);
3444 }
3445 
3446 /*@
3447   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3448 
3449   Not Collective
3450 
3451   Input Parameters:
3452 + mesh - The `DMPLEX`
3453 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3454 - support - An array of points which are on the out-edges for point `p`
3455 
3456   Level: beginner
3457 
3458   Note:
3459   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3460 
3461 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3462 @*/
3463 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3464 {
3465   DM_Plex *mesh = (DM_Plex *)dm->data;
3466   PetscInt pStart, pEnd;
3467   PetscInt dof, off, c;
3468 
3469   PetscFunctionBegin;
3470   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3471   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3472   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3473   if (dof) PetscValidIntPointer(support, 3);
3474   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3475   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);
3476   for (c = 0; c < dof; ++c) {
3477     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);
3478     mesh->supports[off + c] = support[c];
3479   }
3480   PetscFunctionReturn(PETSC_SUCCESS);
3481 }
3482 
3483 /*@
3484   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3485 
3486   Not Collective
3487 
3488   Input Parameters:
3489 + mesh - The `DMPLEX`
3490 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3491 . supportPos - The local index in the cone where the point should be put
3492 - supportPoint - The mesh point to insert
3493 
3494   Level: beginner
3495 
3496 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3497 @*/
3498 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3499 {
3500   DM_Plex *mesh = (DM_Plex *)dm->data;
3501   PetscInt pStart, pEnd;
3502   PetscInt dof, off;
3503 
3504   PetscFunctionBegin;
3505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3506   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3507   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3508   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3509   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);
3510   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);
3511   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);
3512   mesh->supports[off + supportPos] = supportPoint;
3513   PetscFunctionReturn(PETSC_SUCCESS);
3514 }
3515 
3516 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3517 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3518 {
3519   switch (ct) {
3520   case DM_POLYTOPE_SEGMENT:
3521     if (o == -1) return -2;
3522     break;
3523   case DM_POLYTOPE_TRIANGLE:
3524     if (o == -3) return -1;
3525     if (o == -2) return -3;
3526     if (o == -1) return -2;
3527     break;
3528   case DM_POLYTOPE_QUADRILATERAL:
3529     if (o == -4) return -2;
3530     if (o == -3) return -1;
3531     if (o == -2) return -4;
3532     if (o == -1) return -3;
3533     break;
3534   default:
3535     return o;
3536   }
3537   return o;
3538 }
3539 
3540 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3541 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3542 {
3543   switch (ct) {
3544   case DM_POLYTOPE_SEGMENT:
3545     if ((o == -2) || (o == 1)) return -1;
3546     if (o == -1) return 0;
3547     break;
3548   case DM_POLYTOPE_TRIANGLE:
3549     if (o == -3) return -2;
3550     if (o == -2) return -1;
3551     if (o == -1) return -3;
3552     break;
3553   case DM_POLYTOPE_QUADRILATERAL:
3554     if (o == -4) return -2;
3555     if (o == -3) return -1;
3556     if (o == -2) return -4;
3557     if (o == -1) return -3;
3558     break;
3559   default:
3560     return o;
3561   }
3562   return o;
3563 }
3564 
3565 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3566 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3567 {
3568   PetscInt pStart, pEnd, p;
3569 
3570   PetscFunctionBegin;
3571   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3572   for (p = pStart; p < pEnd; ++p) {
3573     const PetscInt *cone, *ornt;
3574     PetscInt        coneSize, c;
3575 
3576     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3577     PetscCall(DMPlexGetCone(dm, p, &cone));
3578     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3579     for (c = 0; c < coneSize; ++c) {
3580       DMPolytopeType ct;
3581       const PetscInt o = ornt[c];
3582 
3583       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3584       switch (ct) {
3585       case DM_POLYTOPE_SEGMENT:
3586         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3587         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3588         break;
3589       case DM_POLYTOPE_TRIANGLE:
3590         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3591         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3592         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3593         break;
3594       case DM_POLYTOPE_QUADRILATERAL:
3595         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3596         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3597         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3598         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3599         break;
3600       default:
3601         break;
3602       }
3603     }
3604   }
3605   PetscFunctionReturn(PETSC_SUCCESS);
3606 }
3607 
3608 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3609 {
3610   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3611   PetscInt       *closure;
3612   const PetscInt *tmp = NULL, *tmpO = NULL;
3613   PetscInt        off = 0, tmpSize, t;
3614 
3615   PetscFunctionBeginHot;
3616   if (ornt) {
3617     PetscCall(DMPlexGetCellType(dm, p, &ct));
3618     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3619   }
3620   if (*points) {
3621     closure = *points;
3622   } else {
3623     PetscInt maxConeSize, maxSupportSize;
3624     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3625     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3626   }
3627   if (useCone) {
3628     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3629     PetscCall(DMPlexGetCone(dm, p, &tmp));
3630     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3631   } else {
3632     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3633     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3634   }
3635   if (ct == DM_POLYTOPE_UNKNOWN) {
3636     closure[off++] = p;
3637     closure[off++] = 0;
3638     for (t = 0; t < tmpSize; ++t) {
3639       closure[off++] = tmp[t];
3640       closure[off++] = tmpO ? tmpO[t] : 0;
3641     }
3642   } else {
3643     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3644 
3645     /* We assume that cells with a valid type have faces with a valid type */
3646     closure[off++] = p;
3647     closure[off++] = ornt;
3648     for (t = 0; t < tmpSize; ++t) {
3649       DMPolytopeType ft;
3650 
3651       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3652       closure[off++] = tmp[arr[t]];
3653       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3654     }
3655   }
3656   if (numPoints) *numPoints = tmpSize + 1;
3657   if (points) *points = closure;
3658   PetscFunctionReturn(PETSC_SUCCESS);
3659 }
3660 
3661 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3662 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3663 {
3664   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3665   const PetscInt *cone, *ornt;
3666   PetscInt       *pts, *closure = NULL;
3667   DMPolytopeType  ft;
3668   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3669   PetscInt        dim, coneSize, c, d, clSize, cl;
3670 
3671   PetscFunctionBeginHot;
3672   PetscCall(DMGetDimension(dm, &dim));
3673   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3674   PetscCall(DMPlexGetCone(dm, point, &cone));
3675   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3676   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3677   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3678   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3679   maxSize       = PetscMax(coneSeries, supportSeries);
3680   if (*points) {
3681     pts = *points;
3682   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3683   c        = 0;
3684   pts[c++] = point;
3685   pts[c++] = o;
3686   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3687   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3688   for (cl = 0; cl < clSize * 2; cl += 2) {
3689     pts[c++] = closure[cl];
3690     pts[c++] = closure[cl + 1];
3691   }
3692   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3693   for (cl = 0; cl < clSize * 2; cl += 2) {
3694     pts[c++] = closure[cl];
3695     pts[c++] = closure[cl + 1];
3696   }
3697   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3698   for (d = 2; d < coneSize; ++d) {
3699     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3700     pts[c++] = cone[arr[d * 2 + 0]];
3701     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3702   }
3703   if (dim >= 3) {
3704     for (d = 2; d < coneSize; ++d) {
3705       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3706       const PetscInt *fcone, *fornt;
3707       PetscInt        fconeSize, fc, i;
3708 
3709       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3710       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3711       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3712       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3713       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3714       for (fc = 0; fc < fconeSize; ++fc) {
3715         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3716         const PetscInt co = farr[fc * 2 + 1];
3717 
3718         for (i = 0; i < c; i += 2)
3719           if (pts[i] == cp) break;
3720         if (i == c) {
3721           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3722           pts[c++] = cp;
3723           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3724         }
3725       }
3726     }
3727   }
3728   *numPoints = c / 2;
3729   *points    = pts;
3730   PetscFunctionReturn(PETSC_SUCCESS);
3731 }
3732 
3733 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3734 {
3735   DMPolytopeType ct;
3736   PetscInt      *closure, *fifo;
3737   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3738   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3739   PetscInt       depth, maxSize;
3740 
3741   PetscFunctionBeginHot;
3742   PetscCall(DMPlexGetDepth(dm, &depth));
3743   if (depth == 1) {
3744     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3745     PetscFunctionReturn(PETSC_SUCCESS);
3746   }
3747   PetscCall(DMPlexGetCellType(dm, p, &ct));
3748   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3749   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3750     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3751     PetscFunctionReturn(PETSC_SUCCESS);
3752   }
3753   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3754   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3755   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3756   maxSize       = PetscMax(coneSeries, supportSeries);
3757   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3758   if (*points) {
3759     closure = *points;
3760   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3761   closure[closureSize++] = p;
3762   closure[closureSize++] = ornt;
3763   fifo[fifoSize++]       = p;
3764   fifo[fifoSize++]       = ornt;
3765   fifo[fifoSize++]       = ct;
3766   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3767   while (fifoSize - fifoStart) {
3768     const PetscInt       q    = fifo[fifoStart++];
3769     const PetscInt       o    = fifo[fifoStart++];
3770     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3771     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3772     const PetscInt      *tmp, *tmpO;
3773     PetscInt             tmpSize, t;
3774 
3775     if (PetscDefined(USE_DEBUG)) {
3776       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3777       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);
3778     }
3779     if (useCone) {
3780       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3781       PetscCall(DMPlexGetCone(dm, q, &tmp));
3782       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3783     } else {
3784       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3785       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3786       tmpO = NULL;
3787     }
3788     for (t = 0; t < tmpSize; ++t) {
3789       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3790       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3791       const PetscInt cp = tmp[ip];
3792       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3793       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3794       PetscInt       c;
3795 
3796       /* Check for duplicate */
3797       for (c = 0; c < closureSize; c += 2) {
3798         if (closure[c] == cp) break;
3799       }
3800       if (c == closureSize) {
3801         closure[closureSize++] = cp;
3802         closure[closureSize++] = co;
3803         fifo[fifoSize++]       = cp;
3804         fifo[fifoSize++]       = co;
3805         fifo[fifoSize++]       = ct;
3806       }
3807     }
3808   }
3809   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3810   if (numPoints) *numPoints = closureSize / 2;
3811   if (points) *points = closure;
3812   PetscFunctionReturn(PETSC_SUCCESS);
3813 }
3814 
3815 /*@C
3816   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3817 
3818   Not Collective
3819 
3820   Input Parameters:
3821 + dm      - The `DMPLEX`
3822 . p       - The mesh point
3823 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3824 
3825   Input/Output Parameter:
3826 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3827            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
3828 
3829   Output Parameter:
3830 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3831 
3832   Level: beginner
3833 
3834   Note:
3835   If using internal storage (points is `NULL` on input), each call overwrites the last output.
3836 
3837   Fortran Note:
3838   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
3839 
3840 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3841 @*/
3842 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3843 {
3844   PetscFunctionBeginHot;
3845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3846   if (numPoints) PetscValidIntPointer(numPoints, 4);
3847   if (points) PetscValidPointer(points, 5);
3848   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3849   PetscFunctionReturn(PETSC_SUCCESS);
3850 }
3851 
3852 /*@C
3853   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3854 
3855   Not Collective
3856 
3857   Input Parameters:
3858 + dm        - The `DMPLEX`
3859 . p         - The mesh point
3860 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3861 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3862 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3863 
3864   Level: beginner
3865 
3866   Note:
3867   If not using internal storage (points is not `NULL` on input), this call is unnecessary
3868 
3869 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3870 @*/
3871 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3872 {
3873   PetscFunctionBeginHot;
3874   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3875   if (numPoints) *numPoints = 0;
3876   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3877   PetscFunctionReturn(PETSC_SUCCESS);
3878 }
3879 
3880 /*@
3881   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3882 
3883   Not Collective
3884 
3885   Input Parameter:
3886 . mesh - The `DMPLEX`
3887 
3888   Output Parameters:
3889 + maxConeSize - The maximum number of in-edges
3890 - maxSupportSize - The maximum number of out-edges
3891 
3892   Level: beginner
3893 
3894 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3895 @*/
3896 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3897 {
3898   DM_Plex *mesh = (DM_Plex *)dm->data;
3899 
3900   PetscFunctionBegin;
3901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3902   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3903   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3904   PetscFunctionReturn(PETSC_SUCCESS);
3905 }
3906 
3907 PetscErrorCode DMSetUp_Plex(DM dm)
3908 {
3909   DM_Plex *mesh = (DM_Plex *)dm->data;
3910   PetscInt size, maxSupportSize;
3911 
3912   PetscFunctionBegin;
3913   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3914   PetscCall(PetscSectionSetUp(mesh->coneSection));
3915   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3916   PetscCall(PetscMalloc1(size, &mesh->cones));
3917   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3918   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3919   if (maxSupportSize) {
3920     PetscCall(PetscSectionSetUp(mesh->supportSection));
3921     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3922     PetscCall(PetscMalloc1(size, &mesh->supports));
3923   }
3924   PetscFunctionReturn(PETSC_SUCCESS);
3925 }
3926 
3927 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3928 {
3929   PetscFunctionBegin;
3930   if (subdm) PetscCall(DMClone(dm, subdm));
3931   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3932   if (subdm) (*subdm)->useNatural = dm->useNatural;
3933   if (dm->useNatural && dm->sfMigration) {
3934     PetscSF sfNatural;
3935 
3936     (*subdm)->sfMigration = dm->sfMigration;
3937     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3938     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3939     (*subdm)->sfNatural = sfNatural;
3940   }
3941   PetscFunctionReturn(PETSC_SUCCESS);
3942 }
3943 
3944 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3945 {
3946   PetscInt i = 0;
3947 
3948   PetscFunctionBegin;
3949   PetscCall(DMClone(dms[0], superdm));
3950   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3951   (*superdm)->useNatural = PETSC_FALSE;
3952   for (i = 0; i < len; i++) {
3953     if (dms[i]->useNatural && dms[i]->sfMigration) {
3954       PetscSF sfNatural;
3955 
3956       (*superdm)->sfMigration = dms[i]->sfMigration;
3957       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3958       (*superdm)->useNatural = PETSC_TRUE;
3959       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3960       (*superdm)->sfNatural = sfNatural;
3961       break;
3962     }
3963   }
3964   PetscFunctionReturn(PETSC_SUCCESS);
3965 }
3966 
3967 /*@
3968   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3969 
3970   Not Collective
3971 
3972   Input Parameter:
3973 . mesh - The `DMPLEX`
3974 
3975   Level: beginner
3976 
3977   Note:
3978   This should be called after all calls to `DMPlexSetCone()`
3979 
3980 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3981 @*/
3982 PetscErrorCode DMPlexSymmetrize(DM dm)
3983 {
3984   DM_Plex  *mesh = (DM_Plex *)dm->data;
3985   PetscInt *offsets;
3986   PetscInt  supportSize;
3987   PetscInt  pStart, pEnd, p;
3988 
3989   PetscFunctionBegin;
3990   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3991   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3992   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3993   /* Calculate support sizes */
3994   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3995   for (p = pStart; p < pEnd; ++p) {
3996     PetscInt dof, off, c;
3997 
3998     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3999     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4000     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4001   }
4002   PetscCall(PetscSectionSetUp(mesh->supportSection));
4003   /* Calculate supports */
4004   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4005   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4006   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4007   for (p = pStart; p < pEnd; ++p) {
4008     PetscInt dof, off, c;
4009 
4010     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4011     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4012     for (c = off; c < off + dof; ++c) {
4013       const PetscInt q = mesh->cones[c];
4014       PetscInt       offS;
4015 
4016       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4017 
4018       mesh->supports[offS + offsets[q]] = p;
4019       ++offsets[q];
4020     }
4021   }
4022   PetscCall(PetscFree(offsets));
4023   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4024   PetscFunctionReturn(PETSC_SUCCESS);
4025 }
4026 
4027 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4028 {
4029   IS stratumIS;
4030 
4031   PetscFunctionBegin;
4032   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4033   if (PetscDefined(USE_DEBUG)) {
4034     PetscInt  qStart, qEnd, numLevels, level;
4035     PetscBool overlap = PETSC_FALSE;
4036     PetscCall(DMLabelGetNumValues(label, &numLevels));
4037     for (level = 0; level < numLevels; level++) {
4038       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4039       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4040         overlap = PETSC_TRUE;
4041         break;
4042       }
4043     }
4044     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);
4045   }
4046   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4047   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4048   PetscCall(ISDestroy(&stratumIS));
4049   PetscFunctionReturn(PETSC_SUCCESS);
4050 }
4051 
4052 /*@
4053   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4054   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4055   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4056   the DAG.
4057 
4058   Collective
4059 
4060   Input Parameter:
4061 . mesh - The `DMPLEX`
4062 
4063   Level: beginner
4064 
4065   Notes:
4066   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4067   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4068   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4069   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4070   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4071 
4072   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4073   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4074   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
4075   to interpolate only that one (e0), so that
4076 .vb
4077   cone(c0) = {e0, v2}
4078   cone(e0) = {v0, v1}
4079 .ve
4080   If `DMPlexStratify()` is run on this mesh, it will give depths
4081 .vb
4082    depth 0 = {v0, v1, v2}
4083    depth 1 = {e0, c0}
4084 .ve
4085   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4086 
4087   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4088 
4089 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4090 @*/
4091 PetscErrorCode DMPlexStratify(DM dm)
4092 {
4093   DM_Plex *mesh = (DM_Plex *)dm->data;
4094   DMLabel  label;
4095   PetscInt pStart, pEnd, p;
4096   PetscInt numRoots = 0, numLeaves = 0;
4097 
4098   PetscFunctionBegin;
4099   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4100   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4101 
4102   /* Create depth label */
4103   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4104   PetscCall(DMCreateLabel(dm, "depth"));
4105   PetscCall(DMPlexGetDepthLabel(dm, &label));
4106 
4107   {
4108     /* Initialize roots and count leaves */
4109     PetscInt sMin = PETSC_MAX_INT;
4110     PetscInt sMax = PETSC_MIN_INT;
4111     PetscInt coneSize, supportSize;
4112 
4113     for (p = pStart; p < pEnd; ++p) {
4114       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4115       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4116       if (!coneSize && supportSize) {
4117         sMin = PetscMin(p, sMin);
4118         sMax = PetscMax(p, sMax);
4119         ++numRoots;
4120       } else if (!supportSize && coneSize) {
4121         ++numLeaves;
4122       } else if (!supportSize && !coneSize) {
4123         /* Isolated points */
4124         sMin = PetscMin(p, sMin);
4125         sMax = PetscMax(p, sMax);
4126       }
4127     }
4128     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4129   }
4130 
4131   if (numRoots + numLeaves == (pEnd - pStart)) {
4132     PetscInt sMin = PETSC_MAX_INT;
4133     PetscInt sMax = PETSC_MIN_INT;
4134     PetscInt coneSize, supportSize;
4135 
4136     for (p = pStart; p < pEnd; ++p) {
4137       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4138       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4139       if (!supportSize && coneSize) {
4140         sMin = PetscMin(p, sMin);
4141         sMax = PetscMax(p, sMax);
4142       }
4143     }
4144     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4145   } else {
4146     PetscInt level = 0;
4147     PetscInt qStart, qEnd, q;
4148 
4149     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4150     while (qEnd > qStart) {
4151       PetscInt sMin = PETSC_MAX_INT;
4152       PetscInt sMax = PETSC_MIN_INT;
4153 
4154       for (q = qStart; q < qEnd; ++q) {
4155         const PetscInt *support;
4156         PetscInt        supportSize, s;
4157 
4158         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4159         PetscCall(DMPlexGetSupport(dm, q, &support));
4160         for (s = 0; s < supportSize; ++s) {
4161           sMin = PetscMin(support[s], sMin);
4162           sMax = PetscMax(support[s], sMax);
4163         }
4164       }
4165       PetscCall(DMLabelGetNumValues(label, &level));
4166       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4167       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4168     }
4169   }
4170   { /* just in case there is an empty process */
4171     PetscInt numValues, maxValues = 0, v;
4172 
4173     PetscCall(DMLabelGetNumValues(label, &numValues));
4174     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4175     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4176   }
4177   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4178   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4179   PetscFunctionReturn(PETSC_SUCCESS);
4180 }
4181 
4182 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4183 {
4184   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4185   PetscInt       dim, depth, pheight, coneSize;
4186 
4187   PetscFunctionBeginHot;
4188   PetscCall(DMGetDimension(dm, &dim));
4189   PetscCall(DMPlexGetDepth(dm, &depth));
4190   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4191   pheight = depth - pdepth;
4192   if (depth <= 1) {
4193     switch (pdepth) {
4194     case 0:
4195       ct = DM_POLYTOPE_POINT;
4196       break;
4197     case 1:
4198       switch (coneSize) {
4199       case 2:
4200         ct = DM_POLYTOPE_SEGMENT;
4201         break;
4202       case 3:
4203         ct = DM_POLYTOPE_TRIANGLE;
4204         break;
4205       case 4:
4206         switch (dim) {
4207         case 2:
4208           ct = DM_POLYTOPE_QUADRILATERAL;
4209           break;
4210         case 3:
4211           ct = DM_POLYTOPE_TETRAHEDRON;
4212           break;
4213         default:
4214           break;
4215         }
4216         break;
4217       case 5:
4218         ct = DM_POLYTOPE_PYRAMID;
4219         break;
4220       case 6:
4221         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4222         break;
4223       case 8:
4224         ct = DM_POLYTOPE_HEXAHEDRON;
4225         break;
4226       default:
4227         break;
4228       }
4229     }
4230   } else {
4231     if (pdepth == 0) {
4232       ct = DM_POLYTOPE_POINT;
4233     } else if (pheight == 0) {
4234       switch (dim) {
4235       case 1:
4236         switch (coneSize) {
4237         case 2:
4238           ct = DM_POLYTOPE_SEGMENT;
4239           break;
4240         default:
4241           break;
4242         }
4243         break;
4244       case 2:
4245         switch (coneSize) {
4246         case 3:
4247           ct = DM_POLYTOPE_TRIANGLE;
4248           break;
4249         case 4:
4250           ct = DM_POLYTOPE_QUADRILATERAL;
4251           break;
4252         default:
4253           break;
4254         }
4255         break;
4256       case 3:
4257         switch (coneSize) {
4258         case 4:
4259           ct = DM_POLYTOPE_TETRAHEDRON;
4260           break;
4261         case 5: {
4262           const PetscInt *cone;
4263           PetscInt        faceConeSize;
4264 
4265           PetscCall(DMPlexGetCone(dm, p, &cone));
4266           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4267           switch (faceConeSize) {
4268           case 3:
4269             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4270             break;
4271           case 4:
4272             ct = DM_POLYTOPE_PYRAMID;
4273             break;
4274           }
4275         } break;
4276         case 6:
4277           ct = DM_POLYTOPE_HEXAHEDRON;
4278           break;
4279         default:
4280           break;
4281         }
4282         break;
4283       default:
4284         break;
4285       }
4286     } else if (pheight > 0) {
4287       switch (coneSize) {
4288       case 2:
4289         ct = DM_POLYTOPE_SEGMENT;
4290         break;
4291       case 3:
4292         ct = DM_POLYTOPE_TRIANGLE;
4293         break;
4294       case 4:
4295         ct = DM_POLYTOPE_QUADRILATERAL;
4296         break;
4297       default:
4298         break;
4299       }
4300     }
4301   }
4302   *pt = ct;
4303   PetscFunctionReturn(PETSC_SUCCESS);
4304 }
4305 
4306 /*@
4307   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4308 
4309   Collective
4310 
4311   Input Parameter:
4312 . mesh - The `DMPLEX`
4313 
4314   Level: developer
4315 
4316   Note:
4317   This function is normally called automatically when a cell type is requested. It creates an
4318   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4319   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4320 
4321   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4322 
4323 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4324 @*/
4325 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4326 {
4327   DM_Plex *mesh;
4328   DMLabel  ctLabel;
4329   PetscInt pStart, pEnd, p;
4330 
4331   PetscFunctionBegin;
4332   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4333   mesh = (DM_Plex *)dm->data;
4334   PetscCall(DMCreateLabel(dm, "celltype"));
4335   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4336   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4337   PetscCall(PetscFree(mesh->cellTypes));
4338   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4339   for (p = pStart; p < pEnd; ++p) {
4340     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4341     PetscInt       pdepth;
4342 
4343     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4344     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4345     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4346     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4347     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4348   }
4349   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4350   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4351   PetscFunctionReturn(PETSC_SUCCESS);
4352 }
4353 
4354 /*@C
4355   DMPlexGetJoin - Get an array for the join of the set of points
4356 
4357   Not Collective
4358 
4359   Input Parameters:
4360 + dm - The `DMPLEX` object
4361 . numPoints - The number of input points for the join
4362 - points - The input points
4363 
4364   Output Parameters:
4365 + numCoveredPoints - The number of points in the join
4366 - coveredPoints - The points in the join
4367 
4368   Level: intermediate
4369 
4370   Note:
4371   Currently, this is restricted to a single level join
4372 
4373   Fortran Note:
4374   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4375 
4376 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4377 @*/
4378 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4379 {
4380   DM_Plex  *mesh = (DM_Plex *)dm->data;
4381   PetscInt *join[2];
4382   PetscInt  joinSize, i = 0;
4383   PetscInt  dof, off, p, c, m;
4384   PetscInt  maxSupportSize;
4385 
4386   PetscFunctionBegin;
4387   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4388   PetscValidIntPointer(points, 3);
4389   PetscValidIntPointer(numCoveredPoints, 4);
4390   PetscValidPointer(coveredPoints, 5);
4391   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4392   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4393   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4394   /* Copy in support of first point */
4395   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4396   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4397   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4398   /* Check each successive support */
4399   for (p = 1; p < numPoints; ++p) {
4400     PetscInt newJoinSize = 0;
4401 
4402     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4403     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4404     for (c = 0; c < dof; ++c) {
4405       const PetscInt point = mesh->supports[off + c];
4406 
4407       for (m = 0; m < joinSize; ++m) {
4408         if (point == join[i][m]) {
4409           join[1 - i][newJoinSize++] = point;
4410           break;
4411         }
4412       }
4413     }
4414     joinSize = newJoinSize;
4415     i        = 1 - i;
4416   }
4417   *numCoveredPoints = joinSize;
4418   *coveredPoints    = join[i];
4419   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4420   PetscFunctionReturn(PETSC_SUCCESS);
4421 }
4422 
4423 /*@C
4424   DMPlexRestoreJoin - Restore an array for the join of the set of points
4425 
4426   Not Collective
4427 
4428   Input Parameters:
4429 + dm - The `DMPLEX` object
4430 . numPoints - The number of input points for the join
4431 - points - The input points
4432 
4433   Output Parameters:
4434 + numCoveredPoints - The number of points in the join
4435 - coveredPoints - The points in the join
4436 
4437   Level: intermediate
4438 
4439   Fortran Note:
4440   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4441 
4442 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4443 @*/
4444 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4445 {
4446   PetscFunctionBegin;
4447   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4448   if (points) PetscValidIntPointer(points, 3);
4449   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4450   PetscValidPointer(coveredPoints, 5);
4451   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4452   if (numCoveredPoints) *numCoveredPoints = 0;
4453   PetscFunctionReturn(PETSC_SUCCESS);
4454 }
4455 
4456 /*@C
4457   DMPlexGetFullJoin - Get an array for the join of the set of points
4458 
4459   Not Collective
4460 
4461   Input Parameters:
4462 + dm - The `DMPLEX` object
4463 . numPoints - The number of input points for the join
4464 - points - The input points
4465 
4466   Output Parameters:
4467 + numCoveredPoints - The number of points in the join
4468 - coveredPoints - The points in the join
4469 
4470   Level: intermediate
4471 
4472   Fortran Note:
4473   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4474 
4475 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4476 @*/
4477 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4478 {
4479   PetscInt *offsets, **closures;
4480   PetscInt *join[2];
4481   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4482   PetscInt  p, d, c, m, ms;
4483 
4484   PetscFunctionBegin;
4485   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4486   PetscValidIntPointer(points, 3);
4487   PetscValidIntPointer(numCoveredPoints, 4);
4488   PetscValidPointer(coveredPoints, 5);
4489 
4490   PetscCall(DMPlexGetDepth(dm, &depth));
4491   PetscCall(PetscCalloc1(numPoints, &closures));
4492   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4493   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4494   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4495   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4496   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4497 
4498   for (p = 0; p < numPoints; ++p) {
4499     PetscInt closureSize;
4500 
4501     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4502 
4503     offsets[p * (depth + 2) + 0] = 0;
4504     for (d = 0; d < depth + 1; ++d) {
4505       PetscInt pStart, pEnd, i;
4506 
4507       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4508       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4509         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4510           offsets[p * (depth + 2) + d + 1] = i;
4511           break;
4512         }
4513       }
4514       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4515     }
4516     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);
4517   }
4518   for (d = 0; d < depth + 1; ++d) {
4519     PetscInt dof;
4520 
4521     /* Copy in support of first point */
4522     dof = offsets[d + 1] - offsets[d];
4523     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4524     /* Check each successive cone */
4525     for (p = 1; p < numPoints && joinSize; ++p) {
4526       PetscInt newJoinSize = 0;
4527 
4528       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4529       for (c = 0; c < dof; ++c) {
4530         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4531 
4532         for (m = 0; m < joinSize; ++m) {
4533           if (point == join[i][m]) {
4534             join[1 - i][newJoinSize++] = point;
4535             break;
4536           }
4537         }
4538       }
4539       joinSize = newJoinSize;
4540       i        = 1 - i;
4541     }
4542     if (joinSize) break;
4543   }
4544   *numCoveredPoints = joinSize;
4545   *coveredPoints    = join[i];
4546   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4547   PetscCall(PetscFree(closures));
4548   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4549   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4550   PetscFunctionReturn(PETSC_SUCCESS);
4551 }
4552 
4553 /*@C
4554   DMPlexGetMeet - Get an array for the meet of the set of points
4555 
4556   Not Collective
4557 
4558   Input Parameters:
4559 + dm - The `DMPLEX` object
4560 . numPoints - The number of input points for the meet
4561 - points - The input points
4562 
4563   Output Parameters:
4564 + numCoveredPoints - The number of points in the meet
4565 - coveredPoints - The points in the meet
4566 
4567   Level: intermediate
4568 
4569   Note:
4570   Currently, this is restricted to a single level meet
4571 
4572   Fortran Notes:
4573   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4574 
4575 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4576 @*/
4577 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4578 {
4579   DM_Plex  *mesh = (DM_Plex *)dm->data;
4580   PetscInt *meet[2];
4581   PetscInt  meetSize, i = 0;
4582   PetscInt  dof, off, p, c, m;
4583   PetscInt  maxConeSize;
4584 
4585   PetscFunctionBegin;
4586   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4587   PetscValidIntPointer(points, 3);
4588   PetscValidIntPointer(numCoveringPoints, 4);
4589   PetscValidPointer(coveringPoints, 5);
4590   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4591   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4592   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4593   /* Copy in cone of first point */
4594   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4595   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4596   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4597   /* Check each successive cone */
4598   for (p = 1; p < numPoints; ++p) {
4599     PetscInt newMeetSize = 0;
4600 
4601     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4602     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4603     for (c = 0; c < dof; ++c) {
4604       const PetscInt point = mesh->cones[off + c];
4605 
4606       for (m = 0; m < meetSize; ++m) {
4607         if (point == meet[i][m]) {
4608           meet[1 - i][newMeetSize++] = point;
4609           break;
4610         }
4611       }
4612     }
4613     meetSize = newMeetSize;
4614     i        = 1 - i;
4615   }
4616   *numCoveringPoints = meetSize;
4617   *coveringPoints    = meet[i];
4618   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4619   PetscFunctionReturn(PETSC_SUCCESS);
4620 }
4621 
4622 /*@C
4623   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4624 
4625   Not Collective
4626 
4627   Input Parameters:
4628 + dm - The `DMPLEX` object
4629 . numPoints - The number of input points for the meet
4630 - points - The input points
4631 
4632   Output Parameters:
4633 + numCoveredPoints - The number of points in the meet
4634 - coveredPoints - The points in the meet
4635 
4636   Level: intermediate
4637 
4638   Fortran Note:
4639   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4640 
4641 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4642 @*/
4643 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4644 {
4645   PetscFunctionBegin;
4646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4647   if (points) PetscValidIntPointer(points, 3);
4648   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4649   PetscValidPointer(coveredPoints, 5);
4650   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4651   if (numCoveredPoints) *numCoveredPoints = 0;
4652   PetscFunctionReturn(PETSC_SUCCESS);
4653 }
4654 
4655 /*@C
4656   DMPlexGetFullMeet - Get an array for the meet of the set of points
4657 
4658   Not Collective
4659 
4660   Input Parameters:
4661 + dm - The `DMPLEX` object
4662 . numPoints - The number of input points for the meet
4663 - points - The input points
4664 
4665   Output Parameters:
4666 + numCoveredPoints - The number of points in the meet
4667 - coveredPoints - The points in the meet
4668 
4669   Level: intermediate
4670 
4671   Fortran Note:
4672   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4673 
4674 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4675 @*/
4676 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4677 {
4678   PetscInt *offsets, **closures;
4679   PetscInt *meet[2];
4680   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4681   PetscInt  p, h, c, m, mc;
4682 
4683   PetscFunctionBegin;
4684   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4685   PetscValidIntPointer(points, 3);
4686   PetscValidIntPointer(numCoveredPoints, 4);
4687   PetscValidPointer(coveredPoints, 5);
4688 
4689   PetscCall(DMPlexGetDepth(dm, &height));
4690   PetscCall(PetscMalloc1(numPoints, &closures));
4691   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4692   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4693   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4694   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4695   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4696 
4697   for (p = 0; p < numPoints; ++p) {
4698     PetscInt closureSize;
4699 
4700     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4701 
4702     offsets[p * (height + 2) + 0] = 0;
4703     for (h = 0; h < height + 1; ++h) {
4704       PetscInt pStart, pEnd, i;
4705 
4706       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4707       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4708         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4709           offsets[p * (height + 2) + h + 1] = i;
4710           break;
4711         }
4712       }
4713       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4714     }
4715     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);
4716   }
4717   for (h = 0; h < height + 1; ++h) {
4718     PetscInt dof;
4719 
4720     /* Copy in cone of first point */
4721     dof = offsets[h + 1] - offsets[h];
4722     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4723     /* Check each successive cone */
4724     for (p = 1; p < numPoints && meetSize; ++p) {
4725       PetscInt newMeetSize = 0;
4726 
4727       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4728       for (c = 0; c < dof; ++c) {
4729         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4730 
4731         for (m = 0; m < meetSize; ++m) {
4732           if (point == meet[i][m]) {
4733             meet[1 - i][newMeetSize++] = point;
4734             break;
4735           }
4736         }
4737       }
4738       meetSize = newMeetSize;
4739       i        = 1 - i;
4740     }
4741     if (meetSize) break;
4742   }
4743   *numCoveredPoints = meetSize;
4744   *coveredPoints    = meet[i];
4745   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4746   PetscCall(PetscFree(closures));
4747   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4748   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4749   PetscFunctionReturn(PETSC_SUCCESS);
4750 }
4751 
4752 /*@C
4753   DMPlexEqual - Determine if two `DM` have the same topology
4754 
4755   Not Collective
4756 
4757   Input Parameters:
4758 + dmA - A `DMPLEX` object
4759 - dmB - A `DMPLEX` object
4760 
4761   Output Parameter:
4762 . equal - `PETSC_TRUE` if the topologies are identical
4763 
4764   Level: intermediate
4765 
4766   Note:
4767   We are not solving graph isomorphism, so we do not permute.
4768 
4769 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4770 @*/
4771 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4772 {
4773   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4774 
4775   PetscFunctionBegin;
4776   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4777   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4778   PetscValidBoolPointer(equal, 3);
4779 
4780   *equal = PETSC_FALSE;
4781   PetscCall(DMPlexGetDepth(dmA, &depth));
4782   PetscCall(DMPlexGetDepth(dmB, &depthB));
4783   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4784   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4785   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4786   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4787   for (p = pStart; p < pEnd; ++p) {
4788     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4789     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4790 
4791     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4792     PetscCall(DMPlexGetCone(dmA, p, &cone));
4793     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4794     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4795     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4796     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4797     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4798     for (c = 0; c < coneSize; ++c) {
4799       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4800       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4801     }
4802     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4803     PetscCall(DMPlexGetSupport(dmA, p, &support));
4804     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4805     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4806     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4807     for (s = 0; s < supportSize; ++s) {
4808       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4809     }
4810   }
4811   *equal = PETSC_TRUE;
4812   PetscFunctionReturn(PETSC_SUCCESS);
4813 }
4814 
4815 /*@C
4816   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4817 
4818   Not Collective
4819 
4820   Input Parameters:
4821 + dm         - The `DMPLEX`
4822 . cellDim    - The cell dimension
4823 - numCorners - The number of vertices on a cell
4824 
4825   Output Parameter:
4826 . numFaceVertices - The number of vertices on a face
4827 
4828   Level: developer
4829 
4830   Note:
4831   Of course this can only work for a restricted set of symmetric shapes
4832 
4833 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4834 @*/
4835 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4836 {
4837   MPI_Comm comm;
4838 
4839   PetscFunctionBegin;
4840   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4841   PetscValidIntPointer(numFaceVertices, 4);
4842   switch (cellDim) {
4843   case 0:
4844     *numFaceVertices = 0;
4845     break;
4846   case 1:
4847     *numFaceVertices = 1;
4848     break;
4849   case 2:
4850     switch (numCorners) {
4851     case 3:                 /* triangle */
4852       *numFaceVertices = 2; /* Edge has 2 vertices */
4853       break;
4854     case 4:                 /* quadrilateral */
4855       *numFaceVertices = 2; /* Edge has 2 vertices */
4856       break;
4857     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4858       *numFaceVertices = 3; /* Edge has 3 vertices */
4859       break;
4860     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4861       *numFaceVertices = 3; /* Edge has 3 vertices */
4862       break;
4863     default:
4864       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4865     }
4866     break;
4867   case 3:
4868     switch (numCorners) {
4869     case 4:                 /* tetradehdron */
4870       *numFaceVertices = 3; /* Face has 3 vertices */
4871       break;
4872     case 6:                 /* tet cohesive cells */
4873       *numFaceVertices = 4; /* Face has 4 vertices */
4874       break;
4875     case 8:                 /* hexahedron */
4876       *numFaceVertices = 4; /* Face has 4 vertices */
4877       break;
4878     case 9:                 /* tet cohesive Lagrange cells */
4879       *numFaceVertices = 6; /* Face has 6 vertices */
4880       break;
4881     case 10:                /* quadratic tetrahedron */
4882       *numFaceVertices = 6; /* Face has 6 vertices */
4883       break;
4884     case 12:                /* hex cohesive Lagrange cells */
4885       *numFaceVertices = 6; /* Face has 6 vertices */
4886       break;
4887     case 18:                /* quadratic tet cohesive Lagrange cells */
4888       *numFaceVertices = 6; /* Face has 6 vertices */
4889       break;
4890     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4891       *numFaceVertices = 9; /* Face has 9 vertices */
4892       break;
4893     default:
4894       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4895     }
4896     break;
4897   default:
4898     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4899   }
4900   PetscFunctionReturn(PETSC_SUCCESS);
4901 }
4902 
4903 /*@
4904   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4905 
4906   Not Collective
4907 
4908   Input Parameter:
4909 . dm    - The `DMPLEX` object
4910 
4911   Output Parameter:
4912 . depthLabel - The `DMLabel` recording point depth
4913 
4914   Level: developer
4915 
4916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4917 @*/
4918 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4919 {
4920   PetscFunctionBegin;
4921   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4922   PetscValidPointer(depthLabel, 2);
4923   *depthLabel = dm->depthLabel;
4924   PetscFunctionReturn(PETSC_SUCCESS);
4925 }
4926 
4927 /*@
4928   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4929 
4930   Not Collective
4931 
4932   Input Parameter:
4933 . dm    - The `DMPLEX` object
4934 
4935   Output Parameter:
4936 . depth - The number of strata (breadth first levels) in the DAG
4937 
4938   Level: developer
4939 
4940   Notes:
4941   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4942 
4943   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4944 
4945   An empty mesh gives -1.
4946 
4947 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4948 @*/
4949 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4950 {
4951   DM_Plex *mesh = (DM_Plex *)dm->data;
4952   DMLabel  label;
4953   PetscInt d = 0;
4954 
4955   PetscFunctionBegin;
4956   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4957   PetscValidIntPointer(depth, 2);
4958   if (mesh->tr) {
4959     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4960   } else {
4961     PetscCall(DMPlexGetDepthLabel(dm, &label));
4962     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4963     *depth = d - 1;
4964   }
4965   PetscFunctionReturn(PETSC_SUCCESS);
4966 }
4967 
4968 /*@
4969   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
4970 
4971   Not Collective
4972 
4973   Input Parameters:
4974 + dm    - The `DMPLEX` object
4975 - depth - The requested depth
4976 
4977   Output Parameters:
4978 + start - The first point at this `depth`
4979 - end   - One beyond the last point at this `depth`
4980 
4981   Level: developer
4982 
4983   Notes:
4984   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4985   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
4986   higher dimension, e.g., "edges".
4987 
4988 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4989 @*/
4990 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4991 {
4992   DM_Plex *mesh = (DM_Plex *)dm->data;
4993   DMLabel  label;
4994   PetscInt pStart, pEnd;
4995 
4996   PetscFunctionBegin;
4997   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4998   if (start) {
4999     PetscValidIntPointer(start, 3);
5000     *start = 0;
5001   }
5002   if (end) {
5003     PetscValidIntPointer(end, 4);
5004     *end = 0;
5005   }
5006   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5007   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5008   if (depth < 0) {
5009     if (start) *start = pStart;
5010     if (end) *end = pEnd;
5011     PetscFunctionReturn(PETSC_SUCCESS);
5012   }
5013   if (mesh->tr) {
5014     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5015   } else {
5016     PetscCall(DMPlexGetDepthLabel(dm, &label));
5017     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5018     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5019   }
5020   PetscFunctionReturn(PETSC_SUCCESS);
5021 }
5022 
5023 /*@
5024   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5025 
5026   Not Collective
5027 
5028   Input Parameters:
5029 + dm     - The `DMPLEX` object
5030 - height - The requested height
5031 
5032   Output Parameters:
5033 + start - The first point at this `height`
5034 - end   - One beyond the last point at this `height`
5035 
5036   Level: developer
5037 
5038   Notes:
5039   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5040   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5041   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5042 
5043 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5044 @*/
5045 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5046 {
5047   DMLabel  label;
5048   PetscInt depth, pStart, pEnd;
5049 
5050   PetscFunctionBegin;
5051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5052   if (start) {
5053     PetscValidIntPointer(start, 3);
5054     *start = 0;
5055   }
5056   if (end) {
5057     PetscValidIntPointer(end, 4);
5058     *end = 0;
5059   }
5060   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5061   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5062   if (height < 0) {
5063     if (start) *start = pStart;
5064     if (end) *end = pEnd;
5065     PetscFunctionReturn(PETSC_SUCCESS);
5066   }
5067   PetscCall(DMPlexGetDepthLabel(dm, &label));
5068   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5069   PetscCall(DMLabelGetNumValues(label, &depth));
5070   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5071   PetscFunctionReturn(PETSC_SUCCESS);
5072 }
5073 
5074 /*@
5075   DMPlexGetPointDepth - Get the `depth` of a given point
5076 
5077   Not Collective
5078 
5079   Input Parameters:
5080 + dm    - The `DMPLEX` object
5081 - point - The point
5082 
5083   Output Parameter:
5084 . depth - The depth of the `point`
5085 
5086   Level: intermediate
5087 
5088 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5089 @*/
5090 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5091 {
5092   PetscFunctionBegin;
5093   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5094   PetscValidIntPointer(depth, 3);
5095   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5096   PetscFunctionReturn(PETSC_SUCCESS);
5097 }
5098 
5099 /*@
5100   DMPlexGetPointHeight - Get the `height` of a given point
5101 
5102   Not Collective
5103 
5104   Input Parameters:
5105 + dm    - The `DMPLEX` object
5106 - point - The point
5107 
5108   Output Parameter:
5109 . height - The height of the `point`
5110 
5111   Level: intermediate
5112 
5113 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5114 @*/
5115 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5116 {
5117   PetscInt n, pDepth;
5118 
5119   PetscFunctionBegin;
5120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5121   PetscValidIntPointer(height, 3);
5122   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5123   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5124   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5125   PetscFunctionReturn(PETSC_SUCCESS);
5126 }
5127 
5128 /*@
5129   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5130 
5131   Not Collective
5132 
5133   Input Parameter:
5134 . dm - The `DMPLEX` object
5135 
5136   Output Parameter:
5137 . celltypeLabel - The `DMLabel` recording cell polytope type
5138 
5139   Level: developer
5140 
5141   Note:
5142   This function will trigger automatica computation of cell types. This can be disabled by calling
5143   `DMCreateLabel`(dm, "celltype") beforehand.
5144 
5145 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5146 @*/
5147 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5148 {
5149   PetscFunctionBegin;
5150   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5151   PetscValidPointer(celltypeLabel, 2);
5152   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5153   *celltypeLabel = dm->celltypeLabel;
5154   PetscFunctionReturn(PETSC_SUCCESS);
5155 }
5156 
5157 /*@
5158   DMPlexGetCellType - Get the polytope type of a given cell
5159 
5160   Not Collective
5161 
5162   Input Parameters:
5163 + dm   - The `DMPLEX` object
5164 - cell - The cell
5165 
5166   Output Parameter:
5167 . celltype - The polytope type of the cell
5168 
5169   Level: intermediate
5170 
5171 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5172 @*/
5173 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5174 {
5175   DM_Plex *mesh = (DM_Plex *)dm->data;
5176   DMLabel  label;
5177   PetscInt ct;
5178 
5179   PetscFunctionBegin;
5180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5181   PetscValidPointer(celltype, 3);
5182   if (mesh->tr) {
5183     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5184   } else {
5185     PetscInt pStart, pEnd;
5186 
5187     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5188     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5189       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5190       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5191       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5192       for (PetscInt p = pStart; p < pEnd; p++) {
5193         PetscCall(DMLabelGetValue(label, p, &ct));
5194         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5195       }
5196     }
5197     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5198     if (PetscDefined(USE_DEBUG)) {
5199       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5200       PetscCall(DMLabelGetValue(label, cell, &ct));
5201       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5202       PetscCheck(ct == *celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5203     }
5204   }
5205   PetscFunctionReturn(PETSC_SUCCESS);
5206 }
5207 
5208 /*@
5209   DMPlexSetCellType - Set the polytope type of a given cell
5210 
5211   Not Collective
5212 
5213   Input Parameters:
5214 + dm   - The `DMPLEX` object
5215 . cell - The cell
5216 - celltype - The polytope type of the cell
5217 
5218   Level: advanced
5219 
5220   Note:
5221   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5222   is executed. This function will override the computed type. However, if automatic classification will not succeed
5223   and a user wants to manually specify all types, the classification must be disabled by calling
5224   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5225 
5226 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5227 @*/
5228 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5229 {
5230   DM_Plex *mesh = (DM_Plex *)dm->data;
5231   DMLabel  label;
5232   PetscInt pStart, pEnd;
5233 
5234   PetscFunctionBegin;
5235   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5236   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5237   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5238   PetscCall(DMLabelSetValue(label, cell, celltype));
5239   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5240   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5241   PetscFunctionReturn(PETSC_SUCCESS);
5242 }
5243 
5244 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5245 {
5246   PetscSection section, s;
5247   Mat          m;
5248   PetscInt     maxHeight;
5249   const char  *prefix;
5250 
5251   PetscFunctionBegin;
5252   PetscCall(DMClone(dm, cdm));
5253   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5254   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5255   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5256   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5257   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5258   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5259   PetscCall(DMSetLocalSection(*cdm, section));
5260   PetscCall(PetscSectionDestroy(&section));
5261   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5262   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5263   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5264   PetscCall(PetscSectionDestroy(&s));
5265   PetscCall(MatDestroy(&m));
5266 
5267   PetscCall(DMSetNumFields(*cdm, 1));
5268   PetscCall(DMCreateDS(*cdm));
5269   (*cdm)->cloneOpts = PETSC_TRUE;
5270   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5271   PetscFunctionReturn(PETSC_SUCCESS);
5272 }
5273 
5274 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5275 {
5276   Vec coordsLocal, cellCoordsLocal;
5277   DM  coordsDM, cellCoordsDM;
5278 
5279   PetscFunctionBegin;
5280   *field = NULL;
5281   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5282   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5283   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5284   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5285   if (coordsLocal && coordsDM) {
5286     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5287     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5288   }
5289   PetscFunctionReturn(PETSC_SUCCESS);
5290 }
5291 
5292 /*@C
5293   DMPlexGetConeSection - Return a section which describes the layout of cone data
5294 
5295   Not Collective
5296 
5297   Input Parameter:
5298 . dm        - The `DMPLEX` object
5299 
5300   Output Parameter:
5301 . section - The `PetscSection` object
5302 
5303   Level: developer
5304 
5305 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5306 @*/
5307 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5308 {
5309   DM_Plex *mesh = (DM_Plex *)dm->data;
5310 
5311   PetscFunctionBegin;
5312   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5313   if (section) *section = mesh->coneSection;
5314   PetscFunctionReturn(PETSC_SUCCESS);
5315 }
5316 
5317 /*@C
5318   DMPlexGetSupportSection - Return a section which describes the layout of support data
5319 
5320   Not Collective
5321 
5322   Input Parameter:
5323 . dm        - The `DMPLEX` object
5324 
5325   Output Parameter:
5326 . section - The `PetscSection` object
5327 
5328   Level: developer
5329 
5330 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5331 @*/
5332 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5333 {
5334   DM_Plex *mesh = (DM_Plex *)dm->data;
5335 
5336   PetscFunctionBegin;
5337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5338   if (section) *section = mesh->supportSection;
5339   PetscFunctionReturn(PETSC_SUCCESS);
5340 }
5341 
5342 /*@C
5343   DMPlexGetCones - Return cone data
5344 
5345   Not Collective
5346 
5347   Input Parameter:
5348 . dm        - The `DMPLEX` object
5349 
5350   Output Parameter:
5351 . cones - The cone for each point
5352 
5353   Level: developer
5354 
5355 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5356 @*/
5357 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5358 {
5359   DM_Plex *mesh = (DM_Plex *)dm->data;
5360 
5361   PetscFunctionBegin;
5362   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5363   if (cones) *cones = mesh->cones;
5364   PetscFunctionReturn(PETSC_SUCCESS);
5365 }
5366 
5367 /*@C
5368   DMPlexGetConeOrientations - Return cone orientation data
5369 
5370   Not Collective
5371 
5372   Input Parameter:
5373 . dm        - The `DMPLEX` object
5374 
5375   Output Parameter:
5376 . coneOrientations - The array of cone orientations for all points
5377 
5378   Level: developer
5379 
5380   Notes:
5381   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5382 
5383   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5384 
5385 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5386 @*/
5387 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5388 {
5389   DM_Plex *mesh = (DM_Plex *)dm->data;
5390 
5391   PetscFunctionBegin;
5392   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5393   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5394   PetscFunctionReturn(PETSC_SUCCESS);
5395 }
5396 
5397 /******************************** FEM Support **********************************/
5398 
5399 /*
5400  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5401  representing a line in the section.
5402 */
5403 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5404 {
5405   PetscFunctionBeginHot;
5406   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5407   if (line < 0) {
5408     *k  = 0;
5409     *Nc = 0;
5410   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5411     *k = 1;
5412   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5413     /* An order k SEM disc has k-1 dofs on an edge */
5414     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5415     *k = *k / *Nc + 1;
5416   }
5417   PetscFunctionReturn(PETSC_SUCCESS);
5418 }
5419 
5420 /*@
5421 
5422   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5423   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5424   section provided (or the section of the `DM`).
5425 
5426   Input Parameters:
5427 + dm      - The `DM`
5428 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5429 - section - The `PetscSection` to reorder, or `NULL` for the default section
5430 
5431   Example:
5432   A typical interpolated single-quad mesh might order points as
5433 .vb
5434   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5435 
5436   v4 -- e6 -- v3
5437   |           |
5438   e7    c0    e8
5439   |           |
5440   v1 -- e5 -- v2
5441 .ve
5442 
5443   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5444   dofs in the order of points, e.g.,
5445 .vb
5446     c0 -> [0,1,2,3]
5447     v1 -> [4]
5448     ...
5449     e5 -> [8, 9]
5450 .ve
5451 
5452   which corresponds to the dofs
5453 .vb
5454     6   10  11  7
5455     13  2   3   15
5456     12  0   1   14
5457     4   8   9   5
5458 .ve
5459 
5460   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5461 .vb
5462   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5463 .ve
5464 
5465   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5466 .vb
5467    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5468 .ve
5469 
5470   Level: developer
5471 
5472   Note:
5473   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5474   degree of the basis.
5475 
5476 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5477 @*/
5478 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5479 {
5480   DMLabel   label;
5481   PetscInt  dim, depth = -1, eStart = -1, Nf;
5482   PetscBool vertexchart;
5483 
5484   PetscFunctionBegin;
5485   PetscCall(DMGetDimension(dm, &dim));
5486   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5487   if (point < 0) {
5488     PetscInt sStart, sEnd;
5489 
5490     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5491     point = sEnd - sStart ? sStart : point;
5492   }
5493   PetscCall(DMPlexGetDepthLabel(dm, &label));
5494   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5495   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5496   if (depth == 1) {
5497     eStart = point;
5498   } else if (depth == dim) {
5499     const PetscInt *cone;
5500 
5501     PetscCall(DMPlexGetCone(dm, point, &cone));
5502     if (dim == 2) eStart = cone[0];
5503     else if (dim == 3) {
5504       const PetscInt *cone2;
5505       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5506       eStart = cone2[0];
5507     } 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);
5508   } 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);
5509   { /* Determine whether the chart covers all points or just vertices. */
5510     PetscInt pStart, pEnd, cStart, cEnd;
5511     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5512     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5513     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5514     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5515     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5516   }
5517   PetscCall(PetscSectionGetNumFields(section, &Nf));
5518   for (PetscInt d = 1; d <= dim; d++) {
5519     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5520     PetscInt *perm;
5521 
5522     for (f = 0; f < Nf; ++f) {
5523       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5524       size += PetscPowInt(k + 1, d) * Nc;
5525     }
5526     PetscCall(PetscMalloc1(size, &perm));
5527     for (f = 0; f < Nf; ++f) {
5528       switch (d) {
5529       case 1:
5530         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5531         /*
5532          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5533          We want              [ vtx0; edge of length k-1; vtx1 ]
5534          */
5535         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5536         for (i = 0; i < k - 1; i++)
5537           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5538         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5539         foffset = offset;
5540         break;
5541       case 2:
5542         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5543         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5544         /* The SEM order is
5545 
5546          v_lb, {e_b}, v_rb,
5547          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5548          v_lt, reverse {e_t}, v_rt
5549          */
5550         {
5551           const PetscInt of   = 0;
5552           const PetscInt oeb  = of + PetscSqr(k - 1);
5553           const PetscInt oer  = oeb + (k - 1);
5554           const PetscInt oet  = oer + (k - 1);
5555           const PetscInt oel  = oet + (k - 1);
5556           const PetscInt ovlb = oel + (k - 1);
5557           const PetscInt ovrb = ovlb + 1;
5558           const PetscInt ovrt = ovrb + 1;
5559           const PetscInt ovlt = ovrt + 1;
5560           PetscInt       o;
5561 
5562           /* bottom */
5563           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5564           for (o = oeb; o < oer; ++o)
5565             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5566           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5567           /* middle */
5568           for (i = 0; i < k - 1; ++i) {
5569             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5570             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5571               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5572             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5573           }
5574           /* top */
5575           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5576           for (o = oel - 1; o >= oet; --o)
5577             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5578           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5579           foffset = offset;
5580         }
5581         break;
5582       case 3:
5583         /* The original hex closure is
5584 
5585          {c,
5586          f_b, f_t, f_f, f_b, f_r, f_l,
5587          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5588          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5589          */
5590         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5591         /* The SEM order is
5592          Bottom Slice
5593          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5594          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5595          v_blb, {e_bb}, v_brb,
5596 
5597          Middle Slice (j)
5598          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5599          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5600          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5601 
5602          Top Slice
5603          v_tlf, {e_tf}, v_trf,
5604          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5605          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5606          */
5607         {
5608           const PetscInt oc    = 0;
5609           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5610           const PetscInt oft   = ofb + PetscSqr(k - 1);
5611           const PetscInt off   = oft + PetscSqr(k - 1);
5612           const PetscInt ofk   = off + PetscSqr(k - 1);
5613           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5614           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5615           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5616           const PetscInt oebb  = oebl + (k - 1);
5617           const PetscInt oebr  = oebb + (k - 1);
5618           const PetscInt oebf  = oebr + (k - 1);
5619           const PetscInt oetf  = oebf + (k - 1);
5620           const PetscInt oetr  = oetf + (k - 1);
5621           const PetscInt oetb  = oetr + (k - 1);
5622           const PetscInt oetl  = oetb + (k - 1);
5623           const PetscInt oerf  = oetl + (k - 1);
5624           const PetscInt oelf  = oerf + (k - 1);
5625           const PetscInt oelb  = oelf + (k - 1);
5626           const PetscInt oerb  = oelb + (k - 1);
5627           const PetscInt ovblf = oerb + (k - 1);
5628           const PetscInt ovblb = ovblf + 1;
5629           const PetscInt ovbrb = ovblb + 1;
5630           const PetscInt ovbrf = ovbrb + 1;
5631           const PetscInt ovtlf = ovbrf + 1;
5632           const PetscInt ovtrf = ovtlf + 1;
5633           const PetscInt ovtrb = ovtrf + 1;
5634           const PetscInt ovtlb = ovtrb + 1;
5635           PetscInt       o, n;
5636 
5637           /* Bottom Slice */
5638           /*   bottom */
5639           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5640           for (o = oetf - 1; o >= oebf; --o)
5641             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5642           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5643           /*   middle */
5644           for (i = 0; i < k - 1; ++i) {
5645             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5646             for (n = 0; n < k - 1; ++n) {
5647               o = ofb + n * (k - 1) + i;
5648               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5649             }
5650             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5651           }
5652           /*   top */
5653           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5654           for (o = oebb; o < oebr; ++o)
5655             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5656           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5657 
5658           /* Middle Slice */
5659           for (j = 0; j < k - 1; ++j) {
5660             /*   bottom */
5661             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5662             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5663               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5664             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5665             /*   middle */
5666             for (i = 0; i < k - 1; ++i) {
5667               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5668               for (n = 0; n < k - 1; ++n)
5669                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5670               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5671             }
5672             /*   top */
5673             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5674             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5675               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5676             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5677           }
5678 
5679           /* Top Slice */
5680           /*   bottom */
5681           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5682           for (o = oetf; o < oetr; ++o)
5683             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5684           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5685           /*   middle */
5686           for (i = 0; i < k - 1; ++i) {
5687             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5688             for (n = 0; n < k - 1; ++n)
5689               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5690             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5691           }
5692           /*   top */
5693           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5694           for (o = oetl - 1; o >= oetb; --o)
5695             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5696           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5697 
5698           foffset = offset;
5699         }
5700         break;
5701       default:
5702         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5703       }
5704     }
5705     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5706     /* Check permutation */
5707     {
5708       PetscInt *check;
5709 
5710       PetscCall(PetscMalloc1(size, &check));
5711       for (i = 0; i < size; ++i) {
5712         check[i] = -1;
5713         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5714       }
5715       for (i = 0; i < size; ++i) check[perm[i]] = i;
5716       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5717       PetscCall(PetscFree(check));
5718     }
5719     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5720     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5721       PetscInt *loc_perm;
5722       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5723       for (PetscInt i = 0; i < size; i++) {
5724         loc_perm[i]        = perm[i];
5725         loc_perm[size + i] = size + perm[i];
5726       }
5727       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5728     }
5729   }
5730   PetscFunctionReturn(PETSC_SUCCESS);
5731 }
5732 
5733 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5734 {
5735   PetscDS  prob;
5736   PetscInt depth, Nf, h;
5737   DMLabel  label;
5738 
5739   PetscFunctionBeginHot;
5740   PetscCall(DMGetDS(dm, &prob));
5741   Nf      = prob->Nf;
5742   label   = dm->depthLabel;
5743   *dspace = NULL;
5744   if (field < Nf) {
5745     PetscObject disc = prob->disc[field];
5746 
5747     if (disc->classid == PETSCFE_CLASSID) {
5748       PetscDualSpace dsp;
5749 
5750       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5751       PetscCall(DMLabelGetNumValues(label, &depth));
5752       PetscCall(DMLabelGetValue(label, point, &h));
5753       h = depth - 1 - h;
5754       if (h) {
5755         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5756       } else {
5757         *dspace = dsp;
5758       }
5759     }
5760   }
5761   PetscFunctionReturn(PETSC_SUCCESS);
5762 }
5763 
5764 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5765 {
5766   PetscScalar       *array;
5767   const PetscScalar *vArray;
5768   const PetscInt    *cone, *coneO;
5769   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5770 
5771   PetscFunctionBeginHot;
5772   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5773   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5774   PetscCall(DMPlexGetCone(dm, point, &cone));
5775   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5776   if (!values || !*values) {
5777     if ((point >= pStart) && (point < pEnd)) {
5778       PetscInt dof;
5779 
5780       PetscCall(PetscSectionGetDof(section, point, &dof));
5781       size += dof;
5782     }
5783     for (p = 0; p < numPoints; ++p) {
5784       const PetscInt cp = cone[p];
5785       PetscInt       dof;
5786 
5787       if ((cp < pStart) || (cp >= pEnd)) continue;
5788       PetscCall(PetscSectionGetDof(section, cp, &dof));
5789       size += dof;
5790     }
5791     if (!values) {
5792       if (csize) *csize = size;
5793       PetscFunctionReturn(PETSC_SUCCESS);
5794     }
5795     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5796   } else {
5797     array = *values;
5798   }
5799   size = 0;
5800   PetscCall(VecGetArrayRead(v, &vArray));
5801   if ((point >= pStart) && (point < pEnd)) {
5802     PetscInt           dof, off, d;
5803     const PetscScalar *varr;
5804 
5805     PetscCall(PetscSectionGetDof(section, point, &dof));
5806     PetscCall(PetscSectionGetOffset(section, point, &off));
5807     varr = &vArray[off];
5808     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5809     size += dof;
5810   }
5811   for (p = 0; p < numPoints; ++p) {
5812     const PetscInt     cp = cone[p];
5813     PetscInt           o  = coneO[p];
5814     PetscInt           dof, off, d;
5815     const PetscScalar *varr;
5816 
5817     if ((cp < pStart) || (cp >= pEnd)) continue;
5818     PetscCall(PetscSectionGetDof(section, cp, &dof));
5819     PetscCall(PetscSectionGetOffset(section, cp, &off));
5820     varr = &vArray[off];
5821     if (o >= 0) {
5822       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5823     } else {
5824       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5825     }
5826     size += dof;
5827   }
5828   PetscCall(VecRestoreArrayRead(v, &vArray));
5829   if (!*values) {
5830     if (csize) *csize = size;
5831     *values = array;
5832   } else {
5833     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5834     *csize = size;
5835   }
5836   PetscFunctionReturn(PETSC_SUCCESS);
5837 }
5838 
5839 /* Compress out points not in the section */
5840 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5841 {
5842   const PetscInt np = *numPoints;
5843   PetscInt       pStart, pEnd, p, q;
5844 
5845   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5846   for (p = 0, q = 0; p < np; ++p) {
5847     const PetscInt r = points[p * 2];
5848     if ((r >= pStart) && (r < pEnd)) {
5849       points[q * 2]     = r;
5850       points[q * 2 + 1] = points[p * 2 + 1];
5851       ++q;
5852     }
5853   }
5854   *numPoints = q;
5855   return PETSC_SUCCESS;
5856 }
5857 
5858 /* Compressed closure does not apply closure permutation */
5859 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5860 {
5861   const PetscInt *cla = NULL;
5862   PetscInt        np, *pts = NULL;
5863 
5864   PetscFunctionBeginHot;
5865   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5866   if (!ornt && *clPoints) {
5867     PetscInt dof, off;
5868 
5869     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5870     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5871     PetscCall(ISGetIndices(*clPoints, &cla));
5872     np  = dof / 2;
5873     pts = (PetscInt *)&cla[off];
5874   } else {
5875     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
5876     PetscCall(CompressPoints_Private(section, &np, pts));
5877   }
5878   *numPoints = np;
5879   *points    = pts;
5880   *clp       = cla;
5881   PetscFunctionReturn(PETSC_SUCCESS);
5882 }
5883 
5884 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5885 {
5886   PetscFunctionBeginHot;
5887   if (!*clPoints) {
5888     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5889   } else {
5890     PetscCall(ISRestoreIndices(*clPoints, clp));
5891   }
5892   *numPoints = 0;
5893   *points    = NULL;
5894   *clSec     = NULL;
5895   *clPoints  = NULL;
5896   *clp       = NULL;
5897   PetscFunctionReturn(PETSC_SUCCESS);
5898 }
5899 
5900 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5901 {
5902   PetscInt            offset = 0, p;
5903   const PetscInt    **perms  = NULL;
5904   const PetscScalar **flips  = NULL;
5905 
5906   PetscFunctionBeginHot;
5907   *size = 0;
5908   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5909   for (p = 0; p < numPoints; p++) {
5910     const PetscInt     point = points[2 * p];
5911     const PetscInt    *perm  = perms ? perms[p] : NULL;
5912     const PetscScalar *flip  = flips ? flips[p] : NULL;
5913     PetscInt           dof, off, d;
5914     const PetscScalar *varr;
5915 
5916     PetscCall(PetscSectionGetDof(section, point, &dof));
5917     PetscCall(PetscSectionGetOffset(section, point, &off));
5918     varr = &vArray[off];
5919     if (clperm) {
5920       if (perm) {
5921         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5922       } else {
5923         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5924       }
5925       if (flip) {
5926         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5927       }
5928     } else {
5929       if (perm) {
5930         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5931       } else {
5932         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5933       }
5934       if (flip) {
5935         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5936       }
5937     }
5938     offset += dof;
5939   }
5940   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5941   *size = offset;
5942   PetscFunctionReturn(PETSC_SUCCESS);
5943 }
5944 
5945 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[])
5946 {
5947   PetscInt offset = 0, f;
5948 
5949   PetscFunctionBeginHot;
5950   *size = 0;
5951   for (f = 0; f < numFields; ++f) {
5952     PetscInt            p;
5953     const PetscInt    **perms = NULL;
5954     const PetscScalar **flips = NULL;
5955 
5956     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5957     for (p = 0; p < numPoints; p++) {
5958       const PetscInt     point = points[2 * p];
5959       PetscInt           fdof, foff, b;
5960       const PetscScalar *varr;
5961       const PetscInt    *perm = perms ? perms[p] : NULL;
5962       const PetscScalar *flip = flips ? flips[p] : NULL;
5963 
5964       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5965       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5966       varr = &vArray[foff];
5967       if (clperm) {
5968         if (perm) {
5969           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5970         } else {
5971           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5972         }
5973         if (flip) {
5974           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5975         }
5976       } else {
5977         if (perm) {
5978           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5979         } else {
5980           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5981         }
5982         if (flip) {
5983           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5984         }
5985       }
5986       offset += fdof;
5987     }
5988     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5989   }
5990   *size = offset;
5991   PetscFunctionReturn(PETSC_SUCCESS);
5992 }
5993 
5994 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
5995 {
5996   PetscSection    clSection;
5997   IS              clPoints;
5998   PetscInt       *points = NULL;
5999   const PetscInt *clp, *perm;
6000   PetscInt        depth, numFields, numPoints, asize;
6001 
6002   PetscFunctionBeginHot;
6003   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6004   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6005   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6006   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6007   PetscCall(DMPlexGetDepth(dm, &depth));
6008   PetscCall(PetscSectionGetNumFields(section, &numFields));
6009   if (depth == 1 && numFields < 2) {
6010     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6011     PetscFunctionReturn(PETSC_SUCCESS);
6012   }
6013   /* Get points */
6014   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6015   /* Get sizes */
6016   asize = 0;
6017   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6018     PetscInt dof;
6019     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6020     asize += dof;
6021   }
6022   if (values) {
6023     const PetscScalar *vArray;
6024     PetscInt           size;
6025 
6026     if (*values) {
6027       PetscCheck(*csize >= asize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Provided array size %" PetscInt_FMT " not sufficient to hold closure size %" PetscInt_FMT, *csize, asize);
6028     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6029     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6030     PetscCall(VecGetArrayRead(v, &vArray));
6031     /* Get values */
6032     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6033     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6034     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6035     /* Cleanup array */
6036     PetscCall(VecRestoreArrayRead(v, &vArray));
6037   }
6038   if (csize) *csize = asize;
6039   /* Cleanup points */
6040   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6041   PetscFunctionReturn(PETSC_SUCCESS);
6042 }
6043 
6044 /*@C
6045   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6046 
6047   Not collective
6048 
6049   Input Parameters:
6050 + dm - The `DM`
6051 . section - The section describing the layout in `v`, or `NULL` to use the default section
6052 . v - The local vector
6053 - point - The point in the `DM`
6054 
6055   Input/Output Parameters:
6056 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6057 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6058            if the user provided `NULL`, it is a borrowed array and should not be freed
6059 
6060   Level: intermediate
6061 
6062   Notes:
6063   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6064   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6065   assembly function, and a user may already have allocated storage for this operation.
6066 
6067   A typical use could be
6068 .vb
6069    values = NULL;
6070    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6071    for (cl = 0; cl < clSize; ++cl) {
6072      <Compute on closure>
6073    }
6074    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6075 .ve
6076   or
6077 .vb
6078    PetscMalloc1(clMaxSize, &values);
6079    for (p = pStart; p < pEnd; ++p) {
6080      clSize = clMaxSize;
6081      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6082      for (cl = 0; cl < clSize; ++cl) {
6083        <Compute on closure>
6084      }
6085    }
6086    PetscFree(values);
6087 .ve
6088 
6089   Fortran Note:
6090   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6091 
6092 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6093 @*/
6094 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6095 {
6096   PetscFunctionBeginHot;
6097   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, v, point, 0, csize, values));
6098   PetscFunctionReturn(PETSC_SUCCESS);
6099 }
6100 
6101 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6102 {
6103   DMLabel            depthLabel;
6104   PetscSection       clSection;
6105   IS                 clPoints;
6106   PetscScalar       *array;
6107   const PetscScalar *vArray;
6108   PetscInt          *points = NULL;
6109   const PetscInt    *clp, *perm = NULL;
6110   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6111 
6112   PetscFunctionBeginHot;
6113   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6114   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6115   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6116   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6117   PetscCall(DMPlexGetDepth(dm, &mdepth));
6118   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6119   PetscCall(PetscSectionGetNumFields(section, &numFields));
6120   if (mdepth == 1 && numFields < 2) {
6121     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6122     PetscFunctionReturn(PETSC_SUCCESS);
6123   }
6124   /* Get points */
6125   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6126   for (clsize = 0, p = 0; p < Np; p++) {
6127     PetscInt dof;
6128     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6129     clsize += dof;
6130   }
6131   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6132   /* Filter points */
6133   for (p = 0; p < numPoints * 2; p += 2) {
6134     PetscInt dep;
6135 
6136     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6137     if (dep != depth) continue;
6138     points[Np * 2 + 0] = points[p];
6139     points[Np * 2 + 1] = points[p + 1];
6140     ++Np;
6141   }
6142   /* Get array */
6143   if (!values || !*values) {
6144     PetscInt asize = 0, dof;
6145 
6146     for (p = 0; p < Np * 2; p += 2) {
6147       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6148       asize += dof;
6149     }
6150     if (!values) {
6151       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6152       if (csize) *csize = asize;
6153       PetscFunctionReturn(PETSC_SUCCESS);
6154     }
6155     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6156   } else {
6157     array = *values;
6158   }
6159   PetscCall(VecGetArrayRead(v, &vArray));
6160   /* Get values */
6161   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6162   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6163   /* Cleanup points */
6164   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6165   /* Cleanup array */
6166   PetscCall(VecRestoreArrayRead(v, &vArray));
6167   if (!*values) {
6168     if (csize) *csize = size;
6169     *values = array;
6170   } else {
6171     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6172     *csize = size;
6173   }
6174   PetscFunctionReturn(PETSC_SUCCESS);
6175 }
6176 
6177 /*@C
6178   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6179 
6180   Not collective
6181 
6182   Input Parameters:
6183 + dm - The `DM`
6184 . section - The section describing the layout in `v`, or `NULL` to use the default section
6185 . v - The local vector
6186 . point - The point in the `DM`
6187 . csize - The number of values in the closure, or `NULL`
6188 - values - The array of values, which is a borrowed array and should not be freed
6189 
6190   Level: intermediate
6191 
6192   Note:
6193   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6194 
6195   Fortran Note:
6196   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6197 
6198 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6199 @*/
6200 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6201 {
6202   PetscInt size = 0;
6203 
6204   PetscFunctionBegin;
6205   /* Should work without recalculating size */
6206   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6207   *values = NULL;
6208   PetscFunctionReturn(PETSC_SUCCESS);
6209 }
6210 
6211 static inline void add(PetscScalar *x, PetscScalar y)
6212 {
6213   *x += y;
6214 }
6215 static inline void insert(PetscScalar *x, PetscScalar y)
6216 {
6217   *x = y;
6218 }
6219 
6220 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[])
6221 {
6222   PetscInt        cdof;  /* The number of constraints on this point */
6223   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6224   PetscScalar    *a;
6225   PetscInt        off, cind = 0, k;
6226 
6227   PetscFunctionBegin;
6228   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6229   PetscCall(PetscSectionGetOffset(section, point, &off));
6230   a = &array[off];
6231   if (!cdof || setBC) {
6232     if (clperm) {
6233       if (perm) {
6234         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6235       } else {
6236         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6237       }
6238     } else {
6239       if (perm) {
6240         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6241       } else {
6242         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6243       }
6244     }
6245   } else {
6246     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6247     if (clperm) {
6248       if (perm) {
6249         for (k = 0; k < dof; ++k) {
6250           if ((cind < cdof) && (k == cdofs[cind])) {
6251             ++cind;
6252             continue;
6253           }
6254           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6255         }
6256       } else {
6257         for (k = 0; k < dof; ++k) {
6258           if ((cind < cdof) && (k == cdofs[cind])) {
6259             ++cind;
6260             continue;
6261           }
6262           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6263         }
6264       }
6265     } else {
6266       if (perm) {
6267         for (k = 0; k < dof; ++k) {
6268           if ((cind < cdof) && (k == cdofs[cind])) {
6269             ++cind;
6270             continue;
6271           }
6272           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6273         }
6274       } else {
6275         for (k = 0; k < dof; ++k) {
6276           if ((cind < cdof) && (k == cdofs[cind])) {
6277             ++cind;
6278             continue;
6279           }
6280           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6281         }
6282       }
6283     }
6284   }
6285   PetscFunctionReturn(PETSC_SUCCESS);
6286 }
6287 
6288 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[])
6289 {
6290   PetscInt        cdof;  /* The number of constraints on this point */
6291   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6292   PetscScalar    *a;
6293   PetscInt        off, cind = 0, k;
6294 
6295   PetscFunctionBegin;
6296   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6297   PetscCall(PetscSectionGetOffset(section, point, &off));
6298   a = &array[off];
6299   if (cdof) {
6300     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6301     if (clperm) {
6302       if (perm) {
6303         for (k = 0; k < dof; ++k) {
6304           if ((cind < cdof) && (k == cdofs[cind])) {
6305             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6306             cind++;
6307           }
6308         }
6309       } else {
6310         for (k = 0; k < dof; ++k) {
6311           if ((cind < cdof) && (k == cdofs[cind])) {
6312             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6313             cind++;
6314           }
6315         }
6316       }
6317     } else {
6318       if (perm) {
6319         for (k = 0; k < dof; ++k) {
6320           if ((cind < cdof) && (k == cdofs[cind])) {
6321             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6322             cind++;
6323           }
6324         }
6325       } else {
6326         for (k = 0; k < dof; ++k) {
6327           if ((cind < cdof) && (k == cdofs[cind])) {
6328             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6329             cind++;
6330           }
6331         }
6332       }
6333     }
6334   }
6335   PetscFunctionReturn(PETSC_SUCCESS);
6336 }
6337 
6338 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[])
6339 {
6340   PetscScalar    *a;
6341   PetscInt        fdof, foff, fcdof, foffset = *offset;
6342   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6343   PetscInt        cind = 0, b;
6344 
6345   PetscFunctionBegin;
6346   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6347   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6348   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6349   a = &array[foff];
6350   if (!fcdof || setBC) {
6351     if (clperm) {
6352       if (perm) {
6353         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6354       } else {
6355         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6356       }
6357     } else {
6358       if (perm) {
6359         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6360       } else {
6361         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6362       }
6363     }
6364   } else {
6365     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6366     if (clperm) {
6367       if (perm) {
6368         for (b = 0; b < fdof; b++) {
6369           if ((cind < fcdof) && (b == fcdofs[cind])) {
6370             ++cind;
6371             continue;
6372           }
6373           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6374         }
6375       } else {
6376         for (b = 0; b < fdof; b++) {
6377           if ((cind < fcdof) && (b == fcdofs[cind])) {
6378             ++cind;
6379             continue;
6380           }
6381           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6382         }
6383       }
6384     } else {
6385       if (perm) {
6386         for (b = 0; b < fdof; b++) {
6387           if ((cind < fcdof) && (b == fcdofs[cind])) {
6388             ++cind;
6389             continue;
6390           }
6391           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6392         }
6393       } else {
6394         for (b = 0; b < fdof; b++) {
6395           if ((cind < fcdof) && (b == fcdofs[cind])) {
6396             ++cind;
6397             continue;
6398           }
6399           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6400         }
6401       }
6402     }
6403   }
6404   *offset += fdof;
6405   PetscFunctionReturn(PETSC_SUCCESS);
6406 }
6407 
6408 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[])
6409 {
6410   PetscScalar    *a;
6411   PetscInt        fdof, foff, fcdof, foffset = *offset;
6412   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6413   PetscInt        Nc, cind = 0, ncind = 0, b;
6414   PetscBool       ncSet, fcSet;
6415 
6416   PetscFunctionBegin;
6417   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6418   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6419   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6420   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6421   a = &array[foff];
6422   if (fcdof) {
6423     /* We just override fcdof and fcdofs with Ncc and comps */
6424     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6425     if (clperm) {
6426       if (perm) {
6427         if (comps) {
6428           for (b = 0; b < fdof; b++) {
6429             ncSet = fcSet = PETSC_FALSE;
6430             if (b % Nc == comps[ncind]) {
6431               ncind = (ncind + 1) % Ncc;
6432               ncSet = PETSC_TRUE;
6433             }
6434             if ((cind < fcdof) && (b == fcdofs[cind])) {
6435               ++cind;
6436               fcSet = PETSC_TRUE;
6437             }
6438             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6439           }
6440         } else {
6441           for (b = 0; b < fdof; b++) {
6442             if ((cind < fcdof) && (b == fcdofs[cind])) {
6443               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6444               ++cind;
6445             }
6446           }
6447         }
6448       } else {
6449         if (comps) {
6450           for (b = 0; b < fdof; b++) {
6451             ncSet = fcSet = PETSC_FALSE;
6452             if (b % Nc == comps[ncind]) {
6453               ncind = (ncind + 1) % Ncc;
6454               ncSet = PETSC_TRUE;
6455             }
6456             if ((cind < fcdof) && (b == fcdofs[cind])) {
6457               ++cind;
6458               fcSet = PETSC_TRUE;
6459             }
6460             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6461           }
6462         } else {
6463           for (b = 0; b < fdof; b++) {
6464             if ((cind < fcdof) && (b == fcdofs[cind])) {
6465               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6466               ++cind;
6467             }
6468           }
6469         }
6470       }
6471     } else {
6472       if (perm) {
6473         if (comps) {
6474           for (b = 0; b < fdof; b++) {
6475             ncSet = fcSet = PETSC_FALSE;
6476             if (b % Nc == comps[ncind]) {
6477               ncind = (ncind + 1) % Ncc;
6478               ncSet = PETSC_TRUE;
6479             }
6480             if ((cind < fcdof) && (b == fcdofs[cind])) {
6481               ++cind;
6482               fcSet = PETSC_TRUE;
6483             }
6484             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6485           }
6486         } else {
6487           for (b = 0; b < fdof; b++) {
6488             if ((cind < fcdof) && (b == fcdofs[cind])) {
6489               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6490               ++cind;
6491             }
6492           }
6493         }
6494       } else {
6495         if (comps) {
6496           for (b = 0; b < fdof; b++) {
6497             ncSet = fcSet = PETSC_FALSE;
6498             if (b % Nc == comps[ncind]) {
6499               ncind = (ncind + 1) % Ncc;
6500               ncSet = PETSC_TRUE;
6501             }
6502             if ((cind < fcdof) && (b == fcdofs[cind])) {
6503               ++cind;
6504               fcSet = PETSC_TRUE;
6505             }
6506             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6507           }
6508         } else {
6509           for (b = 0; b < fdof; b++) {
6510             if ((cind < fcdof) && (b == fcdofs[cind])) {
6511               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6512               ++cind;
6513             }
6514           }
6515         }
6516       }
6517     }
6518   }
6519   *offset += fdof;
6520   PetscFunctionReturn(PETSC_SUCCESS);
6521 }
6522 
6523 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6524 {
6525   PetscScalar    *array;
6526   const PetscInt *cone, *coneO;
6527   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6528 
6529   PetscFunctionBeginHot;
6530   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6531   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6532   PetscCall(DMPlexGetCone(dm, point, &cone));
6533   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6534   PetscCall(VecGetArray(v, &array));
6535   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6536     const PetscInt cp = !p ? point : cone[p - 1];
6537     const PetscInt o  = !p ? 0 : coneO[p - 1];
6538 
6539     if ((cp < pStart) || (cp >= pEnd)) {
6540       dof = 0;
6541       continue;
6542     }
6543     PetscCall(PetscSectionGetDof(section, cp, &dof));
6544     /* ADD_VALUES */
6545     {
6546       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6547       PetscScalar    *a;
6548       PetscInt        cdof, coff, cind = 0, k;
6549 
6550       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6551       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6552       a = &array[coff];
6553       if (!cdof) {
6554         if (o >= 0) {
6555           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6556         } else {
6557           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6558         }
6559       } else {
6560         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6561         if (o >= 0) {
6562           for (k = 0; k < dof; ++k) {
6563             if ((cind < cdof) && (k == cdofs[cind])) {
6564               ++cind;
6565               continue;
6566             }
6567             a[k] += values[off + k];
6568           }
6569         } else {
6570           for (k = 0; k < dof; ++k) {
6571             if ((cind < cdof) && (k == cdofs[cind])) {
6572               ++cind;
6573               continue;
6574             }
6575             a[k] += values[off + dof - k - 1];
6576           }
6577         }
6578       }
6579     }
6580   }
6581   PetscCall(VecRestoreArray(v, &array));
6582   PetscFunctionReturn(PETSC_SUCCESS);
6583 }
6584 
6585 /*@C
6586   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6587 
6588   Not collective
6589 
6590   Input Parameters:
6591 + dm - The `DM`
6592 . section - The section describing the layout in `v`, or `NULL` to use the default section
6593 . v - The local vector
6594 . point - The point in the `DM`
6595 . values - The array of values
6596 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6597          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6598 
6599   Level: intermediate
6600 
6601 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6602 @*/
6603 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6604 {
6605   PetscSection    clSection;
6606   IS              clPoints;
6607   PetscScalar    *array;
6608   PetscInt       *points = NULL;
6609   const PetscInt *clp, *clperm = NULL;
6610   PetscInt        depth, numFields, numPoints, p, clsize;
6611 
6612   PetscFunctionBeginHot;
6613   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6614   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6615   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6616   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6617   PetscCall(DMPlexGetDepth(dm, &depth));
6618   PetscCall(PetscSectionGetNumFields(section, &numFields));
6619   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6620     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6621     PetscFunctionReturn(PETSC_SUCCESS);
6622   }
6623   /* Get points */
6624   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6625   for (clsize = 0, p = 0; p < numPoints; p++) {
6626     PetscInt dof;
6627     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6628     clsize += dof;
6629   }
6630   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6631   /* Get array */
6632   PetscCall(VecGetArray(v, &array));
6633   /* Get values */
6634   if (numFields > 0) {
6635     PetscInt offset = 0, f;
6636     for (f = 0; f < numFields; ++f) {
6637       const PetscInt    **perms = NULL;
6638       const PetscScalar **flips = NULL;
6639 
6640       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6641       switch (mode) {
6642       case INSERT_VALUES:
6643         for (p = 0; p < numPoints; p++) {
6644           const PetscInt     point = points[2 * p];
6645           const PetscInt    *perm  = perms ? perms[p] : NULL;
6646           const PetscScalar *flip  = flips ? flips[p] : NULL;
6647           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6648         }
6649         break;
6650       case INSERT_ALL_VALUES:
6651         for (p = 0; p < numPoints; p++) {
6652           const PetscInt     point = points[2 * p];
6653           const PetscInt    *perm  = perms ? perms[p] : NULL;
6654           const PetscScalar *flip  = flips ? flips[p] : NULL;
6655           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6656         }
6657         break;
6658       case INSERT_BC_VALUES:
6659         for (p = 0; p < numPoints; p++) {
6660           const PetscInt     point = points[2 * p];
6661           const PetscInt    *perm  = perms ? perms[p] : NULL;
6662           const PetscScalar *flip  = flips ? flips[p] : NULL;
6663           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6664         }
6665         break;
6666       case ADD_VALUES:
6667         for (p = 0; p < numPoints; p++) {
6668           const PetscInt     point = points[2 * p];
6669           const PetscInt    *perm  = perms ? perms[p] : NULL;
6670           const PetscScalar *flip  = flips ? flips[p] : NULL;
6671           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6672         }
6673         break;
6674       case ADD_ALL_VALUES:
6675         for (p = 0; p < numPoints; p++) {
6676           const PetscInt     point = points[2 * p];
6677           const PetscInt    *perm  = perms ? perms[p] : NULL;
6678           const PetscScalar *flip  = flips ? flips[p] : NULL;
6679           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6680         }
6681         break;
6682       case ADD_BC_VALUES:
6683         for (p = 0; p < numPoints; p++) {
6684           const PetscInt     point = points[2 * p];
6685           const PetscInt    *perm  = perms ? perms[p] : NULL;
6686           const PetscScalar *flip  = flips ? flips[p] : NULL;
6687           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6688         }
6689         break;
6690       default:
6691         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6692       }
6693       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6694     }
6695   } else {
6696     PetscInt            dof, off;
6697     const PetscInt    **perms = NULL;
6698     const PetscScalar **flips = NULL;
6699 
6700     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6701     switch (mode) {
6702     case INSERT_VALUES:
6703       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6704         const PetscInt     point = points[2 * p];
6705         const PetscInt    *perm  = perms ? perms[p] : NULL;
6706         const PetscScalar *flip  = flips ? flips[p] : NULL;
6707         PetscCall(PetscSectionGetDof(section, point, &dof));
6708         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6709       }
6710       break;
6711     case INSERT_ALL_VALUES:
6712       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6713         const PetscInt     point = points[2 * p];
6714         const PetscInt    *perm  = perms ? perms[p] : NULL;
6715         const PetscScalar *flip  = flips ? flips[p] : NULL;
6716         PetscCall(PetscSectionGetDof(section, point, &dof));
6717         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6718       }
6719       break;
6720     case INSERT_BC_VALUES:
6721       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6722         const PetscInt     point = points[2 * p];
6723         const PetscInt    *perm  = perms ? perms[p] : NULL;
6724         const PetscScalar *flip  = flips ? flips[p] : NULL;
6725         PetscCall(PetscSectionGetDof(section, point, &dof));
6726         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6727       }
6728       break;
6729     case ADD_VALUES:
6730       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6731         const PetscInt     point = points[2 * p];
6732         const PetscInt    *perm  = perms ? perms[p] : NULL;
6733         const PetscScalar *flip  = flips ? flips[p] : NULL;
6734         PetscCall(PetscSectionGetDof(section, point, &dof));
6735         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6736       }
6737       break;
6738     case ADD_ALL_VALUES:
6739       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6740         const PetscInt     point = points[2 * p];
6741         const PetscInt    *perm  = perms ? perms[p] : NULL;
6742         const PetscScalar *flip  = flips ? flips[p] : NULL;
6743         PetscCall(PetscSectionGetDof(section, point, &dof));
6744         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6745       }
6746       break;
6747     case ADD_BC_VALUES:
6748       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6749         const PetscInt     point = points[2 * p];
6750         const PetscInt    *perm  = perms ? perms[p] : NULL;
6751         const PetscScalar *flip  = flips ? flips[p] : NULL;
6752         PetscCall(PetscSectionGetDof(section, point, &dof));
6753         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6754       }
6755       break;
6756     default:
6757       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6758     }
6759     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6760   }
6761   /* Cleanup points */
6762   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6763   /* Cleanup array */
6764   PetscCall(VecRestoreArray(v, &array));
6765   PetscFunctionReturn(PETSC_SUCCESS);
6766 }
6767 
6768 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6769 {
6770   const PetscInt *supp, *cone;
6771   PetscScalar    *a;
6772   PetscInt        dim, Ns, dof, off, n = 0;
6773 
6774   PetscFunctionBegin;
6775   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6776   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6777   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6778   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6779   if (PetscDefined(USE_DEBUG)) {
6780     PetscInt vStart, vEnd;
6781 
6782     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6783     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);
6784   }
6785   PetscValidScalarPointer(values, 5);
6786 
6787   PetscCall(DMGetDimension(dm, &dim));
6788   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6789   PetscCall(DMPlexGetSupport(dm, point, &supp));
6790   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);
6791   PetscCall(VecGetArray(v, &a));
6792   PetscCall(PetscSectionGetDof(section, point, &dof));
6793   PetscCall(PetscSectionGetOffset(section, point, &off));
6794   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6795   for (PetscInt d = 0; d < dim; ++d) {
6796     // Left edge
6797     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6798     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6799     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6800     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6801     // Right edge
6802     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6803     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6804     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6805     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6806   }
6807   PetscCall(VecRestoreArray(v, &a));
6808   PetscFunctionReturn(PETSC_SUCCESS);
6809 }
6810 
6811 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6812 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6813 {
6814   PetscFunctionBegin;
6815   *contains = PETSC_TRUE;
6816   if (label) {
6817     PetscInt fdof;
6818 
6819     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6820     if (!*contains) {
6821       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6822       *offset += fdof;
6823       PetscFunctionReturn(PETSC_SUCCESS);
6824     }
6825   }
6826   PetscFunctionReturn(PETSC_SUCCESS);
6827 }
6828 
6829 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6830 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)
6831 {
6832   PetscSection    clSection;
6833   IS              clPoints;
6834   PetscScalar    *array;
6835   PetscInt       *points = NULL;
6836   const PetscInt *clp;
6837   PetscInt        numFields, numPoints, p;
6838   PetscInt        offset = 0, f;
6839 
6840   PetscFunctionBeginHot;
6841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6842   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6843   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6844   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6845   PetscCall(PetscSectionGetNumFields(section, &numFields));
6846   /* Get points */
6847   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6848   /* Get array */
6849   PetscCall(VecGetArray(v, &array));
6850   /* Get values */
6851   for (f = 0; f < numFields; ++f) {
6852     const PetscInt    **perms = NULL;
6853     const PetscScalar **flips = NULL;
6854     PetscBool           contains;
6855 
6856     if (!fieldActive[f]) {
6857       for (p = 0; p < numPoints * 2; p += 2) {
6858         PetscInt fdof;
6859         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6860         offset += fdof;
6861       }
6862       continue;
6863     }
6864     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6865     switch (mode) {
6866     case INSERT_VALUES:
6867       for (p = 0; p < numPoints; p++) {
6868         const PetscInt     point = points[2 * p];
6869         const PetscInt    *perm  = perms ? perms[p] : NULL;
6870         const PetscScalar *flip  = flips ? flips[p] : NULL;
6871         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6872         if (!contains) continue;
6873         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6874       }
6875       break;
6876     case INSERT_ALL_VALUES:
6877       for (p = 0; p < numPoints; p++) {
6878         const PetscInt     point = points[2 * p];
6879         const PetscInt    *perm  = perms ? perms[p] : NULL;
6880         const PetscScalar *flip  = flips ? flips[p] : NULL;
6881         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6882         if (!contains) continue;
6883         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6884       }
6885       break;
6886     case INSERT_BC_VALUES:
6887       for (p = 0; p < numPoints; p++) {
6888         const PetscInt     point = points[2 * p];
6889         const PetscInt    *perm  = perms ? perms[p] : NULL;
6890         const PetscScalar *flip  = flips ? flips[p] : NULL;
6891         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6892         if (!contains) continue;
6893         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6894       }
6895       break;
6896     case ADD_VALUES:
6897       for (p = 0; p < numPoints; p++) {
6898         const PetscInt     point = points[2 * p];
6899         const PetscInt    *perm  = perms ? perms[p] : NULL;
6900         const PetscScalar *flip  = flips ? flips[p] : NULL;
6901         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6902         if (!contains) continue;
6903         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6904       }
6905       break;
6906     case ADD_ALL_VALUES:
6907       for (p = 0; p < numPoints; p++) {
6908         const PetscInt     point = points[2 * p];
6909         const PetscInt    *perm  = perms ? perms[p] : NULL;
6910         const PetscScalar *flip  = flips ? flips[p] : NULL;
6911         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6912         if (!contains) continue;
6913         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6914       }
6915       break;
6916     default:
6917       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6918     }
6919     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6920   }
6921   /* Cleanup points */
6922   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6923   /* Cleanup array */
6924   PetscCall(VecRestoreArray(v, &array));
6925   PetscFunctionReturn(PETSC_SUCCESS);
6926 }
6927 
6928 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6929 {
6930   PetscMPIInt rank;
6931   PetscInt    i, j;
6932 
6933   PetscFunctionBegin;
6934   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6935   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6936   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6937   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6938   numCIndices = numCIndices ? numCIndices : numRIndices;
6939   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6940   for (i = 0; i < numRIndices; i++) {
6941     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6942     for (j = 0; j < numCIndices; j++) {
6943 #if defined(PETSC_USE_COMPLEX)
6944       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6945 #else
6946       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6947 #endif
6948     }
6949     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6950   }
6951   PetscFunctionReturn(PETSC_SUCCESS);
6952 }
6953 
6954 /*
6955   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6956 
6957   Input Parameters:
6958 + section - The section for this data layout
6959 . islocal - Is the section (and thus indices being requested) local or global?
6960 . point   - The point contributing dofs with these indices
6961 . off     - The global offset of this point
6962 . loff    - The local offset of each field
6963 . setBC   - The flag determining whether to include indices of boundary values
6964 . perm    - A permutation of the dofs on this point, or NULL
6965 - indperm - A permutation of the entire indices array, or NULL
6966 
6967   Output Parameter:
6968 . indices - Indices for dofs on this point
6969 
6970   Level: developer
6971 
6972   Note: The indices could be local or global, depending on the value of 'off'.
6973 */
6974 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6975 {
6976   PetscInt        dof;   /* The number of unknowns on this point */
6977   PetscInt        cdof;  /* The number of constraints on this point */
6978   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6979   PetscInt        cind = 0, k;
6980 
6981   PetscFunctionBegin;
6982   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6983   PetscCall(PetscSectionGetDof(section, point, &dof));
6984   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6985   if (!cdof || setBC) {
6986     for (k = 0; k < dof; ++k) {
6987       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6988       const PetscInt ind    = indperm ? indperm[preind] : preind;
6989 
6990       indices[ind] = off + k;
6991     }
6992   } else {
6993     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6994     for (k = 0; k < dof; ++k) {
6995       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6996       const PetscInt ind    = indperm ? indperm[preind] : preind;
6997 
6998       if ((cind < cdof) && (k == cdofs[cind])) {
6999         /* Insert check for returning constrained indices */
7000         indices[ind] = -(off + k + 1);
7001         ++cind;
7002       } else {
7003         indices[ind] = off + k - (islocal ? 0 : cind);
7004       }
7005     }
7006   }
7007   *loff += dof;
7008   PetscFunctionReturn(PETSC_SUCCESS);
7009 }
7010 
7011 /*
7012  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7013 
7014  Input Parameters:
7015 + section - a section (global or local)
7016 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7017 . point - point within section
7018 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7019 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7020 . setBC - identify constrained (boundary condition) points via involution.
7021 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7022 . permsoff - offset
7023 - indperm - index permutation
7024 
7025  Output Parameter:
7026 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7027 . indices - array to hold indices (as defined by section) of each dof associated with point
7028 
7029  Notes:
7030  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7031  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7032  in the local vector.
7033 
7034  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7035  significant).  It is invalid to call with a global section and setBC=true.
7036 
7037  Developer Note:
7038  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7039  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7040  offset could be obtained from the section instead of passing it explicitly as we do now.
7041 
7042  Example:
7043  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7044  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7045  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7046  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.
7047 
7048  Level: developer
7049 */
7050 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[])
7051 {
7052   PetscInt numFields, foff, f;
7053 
7054   PetscFunctionBegin;
7055   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7056   PetscCall(PetscSectionGetNumFields(section, &numFields));
7057   for (f = 0, foff = 0; f < numFields; ++f) {
7058     PetscInt        fdof, cfdof;
7059     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7060     PetscInt        cind = 0, b;
7061     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7062 
7063     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7064     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7065     if (!cfdof || setBC) {
7066       for (b = 0; b < fdof; ++b) {
7067         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7068         const PetscInt ind    = indperm ? indperm[preind] : preind;
7069 
7070         indices[ind] = off + foff + b;
7071       }
7072     } else {
7073       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7074       for (b = 0; b < fdof; ++b) {
7075         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7076         const PetscInt ind    = indperm ? indperm[preind] : preind;
7077 
7078         if ((cind < cfdof) && (b == fcdofs[cind])) {
7079           indices[ind] = -(off + foff + b + 1);
7080           ++cind;
7081         } else {
7082           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7083         }
7084       }
7085     }
7086     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7087     foffs[f] += fdof;
7088   }
7089   PetscFunctionReturn(PETSC_SUCCESS);
7090 }
7091 
7092 /*
7093   This version believes the globalSection offsets for each field, rather than just the point offset
7094 
7095  . foffs - The offset into 'indices' for each field, since it is segregated by field
7096 
7097  Notes:
7098  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7099  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7100 */
7101 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7102 {
7103   PetscInt numFields, foff, f;
7104 
7105   PetscFunctionBegin;
7106   PetscCall(PetscSectionGetNumFields(section, &numFields));
7107   for (f = 0; f < numFields; ++f) {
7108     PetscInt        fdof, cfdof;
7109     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7110     PetscInt        cind = 0, b;
7111     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7112 
7113     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7114     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7115     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7116     if (!cfdof) {
7117       for (b = 0; b < fdof; ++b) {
7118         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7119         const PetscInt ind    = indperm ? indperm[preind] : preind;
7120 
7121         indices[ind] = foff + b;
7122       }
7123     } else {
7124       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7125       for (b = 0; b < fdof; ++b) {
7126         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7127         const PetscInt ind    = indperm ? indperm[preind] : preind;
7128 
7129         if ((cind < cfdof) && (b == fcdofs[cind])) {
7130           indices[ind] = -(foff + b + 1);
7131           ++cind;
7132         } else {
7133           indices[ind] = foff + b - cind;
7134         }
7135       }
7136     }
7137     foffs[f] += fdof;
7138   }
7139   PetscFunctionReturn(PETSC_SUCCESS);
7140 }
7141 
7142 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)
7143 {
7144   Mat             cMat;
7145   PetscSection    aSec, cSec;
7146   IS              aIS;
7147   PetscInt        aStart = -1, aEnd = -1;
7148   const PetscInt *anchors;
7149   PetscInt        numFields, f, p, q, newP = 0;
7150   PetscInt        newNumPoints = 0, newNumIndices = 0;
7151   PetscInt       *newPoints, *indices, *newIndices;
7152   PetscInt        maxAnchor, maxDof;
7153   PetscInt        newOffsets[32];
7154   PetscInt       *pointMatOffsets[32];
7155   PetscInt       *newPointOffsets[32];
7156   PetscScalar    *pointMat[32];
7157   PetscScalar    *newValues      = NULL, *tmpValues;
7158   PetscBool       anyConstrained = PETSC_FALSE;
7159 
7160   PetscFunctionBegin;
7161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7162   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7163   PetscCall(PetscSectionGetNumFields(section, &numFields));
7164 
7165   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7166   /* if there are point-to-point constraints */
7167   if (aSec) {
7168     PetscCall(PetscArrayzero(newOffsets, 32));
7169     PetscCall(ISGetIndices(aIS, &anchors));
7170     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7171     /* figure out how many points are going to be in the new element matrix
7172      * (we allow double counting, because it's all just going to be summed
7173      * into the global matrix anyway) */
7174     for (p = 0; p < 2 * numPoints; p += 2) {
7175       PetscInt b    = points[p];
7176       PetscInt bDof = 0, bSecDof;
7177 
7178       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7179       if (!bSecDof) continue;
7180       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7181       if (bDof) {
7182         /* this point is constrained */
7183         /* it is going to be replaced by its anchors */
7184         PetscInt bOff, q;
7185 
7186         anyConstrained = PETSC_TRUE;
7187         newNumPoints += bDof;
7188         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7189         for (q = 0; q < bDof; q++) {
7190           PetscInt a = anchors[bOff + q];
7191           PetscInt aDof;
7192 
7193           PetscCall(PetscSectionGetDof(section, a, &aDof));
7194           newNumIndices += aDof;
7195           for (f = 0; f < numFields; ++f) {
7196             PetscInt fDof;
7197 
7198             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7199             newOffsets[f + 1] += fDof;
7200           }
7201         }
7202       } else {
7203         /* this point is not constrained */
7204         newNumPoints++;
7205         newNumIndices += bSecDof;
7206         for (f = 0; f < numFields; ++f) {
7207           PetscInt fDof;
7208 
7209           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7210           newOffsets[f + 1] += fDof;
7211         }
7212       }
7213     }
7214   }
7215   if (!anyConstrained) {
7216     if (outNumPoints) *outNumPoints = 0;
7217     if (outNumIndices) *outNumIndices = 0;
7218     if (outPoints) *outPoints = NULL;
7219     if (outValues) *outValues = NULL;
7220     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7221     PetscFunctionReturn(PETSC_SUCCESS);
7222   }
7223 
7224   if (outNumPoints) *outNumPoints = newNumPoints;
7225   if (outNumIndices) *outNumIndices = newNumIndices;
7226 
7227   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7228 
7229   if (!outPoints && !outValues) {
7230     if (offsets) {
7231       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7232     }
7233     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7234     PetscFunctionReturn(PETSC_SUCCESS);
7235   }
7236 
7237   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7238 
7239   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7240 
7241   /* workspaces */
7242   if (numFields) {
7243     for (f = 0; f < numFields; f++) {
7244       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7245       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7246     }
7247   } else {
7248     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7249     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7250   }
7251 
7252   /* get workspaces for the point-to-point matrices */
7253   if (numFields) {
7254     PetscInt totalOffset, totalMatOffset;
7255 
7256     for (p = 0; p < numPoints; p++) {
7257       PetscInt b    = points[2 * p];
7258       PetscInt bDof = 0, bSecDof;
7259 
7260       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7261       if (!bSecDof) {
7262         for (f = 0; f < numFields; f++) {
7263           newPointOffsets[f][p + 1] = 0;
7264           pointMatOffsets[f][p + 1] = 0;
7265         }
7266         continue;
7267       }
7268       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7269       if (bDof) {
7270         for (f = 0; f < numFields; f++) {
7271           PetscInt fDof, q, bOff, allFDof = 0;
7272 
7273           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7274           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7275           for (q = 0; q < bDof; q++) {
7276             PetscInt a = anchors[bOff + q];
7277             PetscInt aFDof;
7278 
7279             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7280             allFDof += aFDof;
7281           }
7282           newPointOffsets[f][p + 1] = allFDof;
7283           pointMatOffsets[f][p + 1] = fDof * allFDof;
7284         }
7285       } else {
7286         for (f = 0; f < numFields; f++) {
7287           PetscInt fDof;
7288 
7289           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7290           newPointOffsets[f][p + 1] = fDof;
7291           pointMatOffsets[f][p + 1] = 0;
7292         }
7293       }
7294     }
7295     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7296       newPointOffsets[f][0] = totalOffset;
7297       pointMatOffsets[f][0] = totalMatOffset;
7298       for (p = 0; p < numPoints; p++) {
7299         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7300         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7301       }
7302       totalOffset    = newPointOffsets[f][numPoints];
7303       totalMatOffset = pointMatOffsets[f][numPoints];
7304       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7305     }
7306   } else {
7307     for (p = 0; p < numPoints; p++) {
7308       PetscInt b    = points[2 * p];
7309       PetscInt bDof = 0, bSecDof;
7310 
7311       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7312       if (!bSecDof) {
7313         newPointOffsets[0][p + 1] = 0;
7314         pointMatOffsets[0][p + 1] = 0;
7315         continue;
7316       }
7317       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7318       if (bDof) {
7319         PetscInt bOff, q, allDof = 0;
7320 
7321         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7322         for (q = 0; q < bDof; q++) {
7323           PetscInt a = anchors[bOff + q], aDof;
7324 
7325           PetscCall(PetscSectionGetDof(section, a, &aDof));
7326           allDof += aDof;
7327         }
7328         newPointOffsets[0][p + 1] = allDof;
7329         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7330       } else {
7331         newPointOffsets[0][p + 1] = bSecDof;
7332         pointMatOffsets[0][p + 1] = 0;
7333       }
7334     }
7335     newPointOffsets[0][0] = 0;
7336     pointMatOffsets[0][0] = 0;
7337     for (p = 0; p < numPoints; p++) {
7338       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7339       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7340     }
7341     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7342   }
7343 
7344   /* output arrays */
7345   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7346 
7347   /* get the point-to-point matrices; construct newPoints */
7348   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7349   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7350   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7351   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7352   if (numFields) {
7353     for (p = 0, newP = 0; p < numPoints; p++) {
7354       PetscInt b    = points[2 * p];
7355       PetscInt o    = points[2 * p + 1];
7356       PetscInt bDof = 0, bSecDof;
7357 
7358       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7359       if (!bSecDof) continue;
7360       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7361       if (bDof) {
7362         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7363 
7364         fStart[0] = 0;
7365         fEnd[0]   = 0;
7366         for (f = 0; f < numFields; f++) {
7367           PetscInt fDof;
7368 
7369           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7370           fStart[f + 1] = fStart[f] + fDof;
7371           fEnd[f + 1]   = fStart[f + 1];
7372         }
7373         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7374         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7375 
7376         fAnchorStart[0] = 0;
7377         fAnchorEnd[0]   = 0;
7378         for (f = 0; f < numFields; f++) {
7379           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7380 
7381           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7382           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7383         }
7384         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7385         for (q = 0; q < bDof; q++) {
7386           PetscInt a = anchors[bOff + q], aOff;
7387 
7388           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7389           newPoints[2 * (newP + q)]     = a;
7390           newPoints[2 * (newP + q) + 1] = 0;
7391           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7392           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7393         }
7394         newP += bDof;
7395 
7396         if (outValues) {
7397           /* get the point-to-point submatrix */
7398           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]));
7399         }
7400       } else {
7401         newPoints[2 * newP]     = b;
7402         newPoints[2 * newP + 1] = o;
7403         newP++;
7404       }
7405     }
7406   } else {
7407     for (p = 0; p < numPoints; p++) {
7408       PetscInt b    = points[2 * p];
7409       PetscInt o    = points[2 * p + 1];
7410       PetscInt bDof = 0, bSecDof;
7411 
7412       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7413       if (!bSecDof) continue;
7414       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7415       if (bDof) {
7416         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7417 
7418         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7419         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7420 
7421         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7422         for (q = 0; q < bDof; q++) {
7423           PetscInt a = anchors[bOff + q], aOff;
7424 
7425           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7426 
7427           newPoints[2 * (newP + q)]     = a;
7428           newPoints[2 * (newP + q) + 1] = 0;
7429           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7430           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7431         }
7432         newP += bDof;
7433 
7434         /* get the point-to-point submatrix */
7435         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7436       } else {
7437         newPoints[2 * newP]     = b;
7438         newPoints[2 * newP + 1] = o;
7439         newP++;
7440       }
7441     }
7442   }
7443 
7444   if (outValues) {
7445     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7446     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7447     /* multiply constraints on the right */
7448     if (numFields) {
7449       for (f = 0; f < numFields; f++) {
7450         PetscInt oldOff = offsets[f];
7451 
7452         for (p = 0; p < numPoints; p++) {
7453           PetscInt cStart = newPointOffsets[f][p];
7454           PetscInt b      = points[2 * p];
7455           PetscInt c, r, k;
7456           PetscInt dof;
7457 
7458           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7459           if (!dof) continue;
7460           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7461             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7462             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7463 
7464             for (r = 0; r < numIndices; r++) {
7465               for (c = 0; c < nCols; c++) {
7466                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7467               }
7468             }
7469           } else {
7470             /* copy this column as is */
7471             for (r = 0; r < numIndices; r++) {
7472               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7473             }
7474           }
7475           oldOff += dof;
7476         }
7477       }
7478     } else {
7479       PetscInt oldOff = 0;
7480       for (p = 0; p < numPoints; p++) {
7481         PetscInt cStart = newPointOffsets[0][p];
7482         PetscInt b      = points[2 * p];
7483         PetscInt c, r, k;
7484         PetscInt dof;
7485 
7486         PetscCall(PetscSectionGetDof(section, b, &dof));
7487         if (!dof) continue;
7488         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7489           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7490           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7491 
7492           for (r = 0; r < numIndices; r++) {
7493             for (c = 0; c < nCols; c++) {
7494               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7495             }
7496           }
7497         } else {
7498           /* copy this column as is */
7499           for (r = 0; r < numIndices; r++) {
7500             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7501           }
7502         }
7503         oldOff += dof;
7504       }
7505     }
7506 
7507     if (multiplyLeft) {
7508       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7509       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7510       /* multiply constraints transpose on the left */
7511       if (numFields) {
7512         for (f = 0; f < numFields; f++) {
7513           PetscInt oldOff = offsets[f];
7514 
7515           for (p = 0; p < numPoints; p++) {
7516             PetscInt rStart = newPointOffsets[f][p];
7517             PetscInt b      = points[2 * p];
7518             PetscInt c, r, k;
7519             PetscInt dof;
7520 
7521             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7522             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7523               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7524               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7525 
7526               for (r = 0; r < nRows; r++) {
7527                 for (c = 0; c < newNumIndices; c++) {
7528                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7529                 }
7530               }
7531             } else {
7532               /* copy this row as is */
7533               for (r = 0; r < dof; r++) {
7534                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7535               }
7536             }
7537             oldOff += dof;
7538           }
7539         }
7540       } else {
7541         PetscInt oldOff = 0;
7542 
7543         for (p = 0; p < numPoints; p++) {
7544           PetscInt rStart = newPointOffsets[0][p];
7545           PetscInt b      = points[2 * p];
7546           PetscInt c, r, k;
7547           PetscInt dof;
7548 
7549           PetscCall(PetscSectionGetDof(section, b, &dof));
7550           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7551             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7552             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7553 
7554             for (r = 0; r < nRows; r++) {
7555               for (c = 0; c < newNumIndices; c++) {
7556                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7557               }
7558             }
7559           } else {
7560             /* copy this row as is */
7561             for (r = 0; r < dof; r++) {
7562               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7563             }
7564           }
7565           oldOff += dof;
7566         }
7567       }
7568 
7569       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7570     } else {
7571       newValues = tmpValues;
7572     }
7573   }
7574 
7575   /* clean up */
7576   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7577   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7578 
7579   if (numFields) {
7580     for (f = 0; f < numFields; f++) {
7581       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7582       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7583       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7584     }
7585   } else {
7586     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7587     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7588     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7589   }
7590   PetscCall(ISRestoreIndices(aIS, &anchors));
7591 
7592   /* output */
7593   if (outPoints) {
7594     *outPoints = newPoints;
7595   } else {
7596     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7597   }
7598   if (outValues) *outValues = newValues;
7599   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7600   PetscFunctionReturn(PETSC_SUCCESS);
7601 }
7602 
7603 /*@C
7604   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7605 
7606   Not collective
7607 
7608   Input Parameters:
7609 + dm         - The `DM`
7610 . section    - The `PetscSection` describing the points (a local section)
7611 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7612 . point      - The point defining the closure
7613 - useClPerm  - Use the closure point permutation if available
7614 
7615   Output Parameters:
7616 + numIndices - The number of dof indices in the closure of point with the input sections
7617 . indices    - The dof indices
7618 . outOffsets - Array to write the field offsets into, or `NULL`
7619 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7620 
7621   Level: advanced
7622 
7623   Notes:
7624   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7625 
7626   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7627   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7628   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7629   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7630   indices (with the above semantics) are implied.
7631 
7632 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7633           `PetscSection`, `DMGetGlobalSection()`
7634 @*/
7635 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7636 {
7637   /* Closure ordering */
7638   PetscSection    clSection;
7639   IS              clPoints;
7640   const PetscInt *clp;
7641   PetscInt       *points;
7642   const PetscInt *clperm = NULL;
7643   /* Dof permutation and sign flips */
7644   const PetscInt    **perms[32] = {NULL};
7645   const PetscScalar **flips[32] = {NULL};
7646   PetscScalar        *valCopy   = NULL;
7647   /* Hanging node constraints */
7648   PetscInt    *pointsC = NULL;
7649   PetscScalar *valuesC = NULL;
7650   PetscInt     NclC, NiC;
7651 
7652   PetscInt *idx;
7653   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7654   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7655 
7656   PetscFunctionBeginHot;
7657   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7658   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7659   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7660   if (numIndices) PetscValidIntPointer(numIndices, 6);
7661   if (indices) PetscValidPointer(indices, 7);
7662   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7663   if (values) PetscValidPointer(values, 9);
7664   PetscCall(PetscSectionGetNumFields(section, &Nf));
7665   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7666   PetscCall(PetscArrayzero(offsets, 32));
7667   /* 1) Get points in closure */
7668   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7669   if (useClPerm) {
7670     PetscInt depth, clsize;
7671     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7672     for (clsize = 0, p = 0; p < Ncl; p++) {
7673       PetscInt dof;
7674       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7675       clsize += dof;
7676     }
7677     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7678   }
7679   /* 2) Get number of indices on these points and field offsets from section */
7680   for (p = 0; p < Ncl * 2; p += 2) {
7681     PetscInt dof, fdof;
7682 
7683     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7684     for (f = 0; f < Nf; ++f) {
7685       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7686       offsets[f + 1] += fdof;
7687     }
7688     Ni += dof;
7689   }
7690   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7691   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7692   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7693   for (f = 0; f < PetscMax(1, Nf); ++f) {
7694     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7695     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7696     /* may need to apply sign changes to the element matrix */
7697     if (values && flips[f]) {
7698       PetscInt foffset = offsets[f];
7699 
7700       for (p = 0; p < Ncl; ++p) {
7701         PetscInt           pnt  = points[2 * p], fdof;
7702         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7703 
7704         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7705         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7706         if (flip) {
7707           PetscInt i, j, k;
7708 
7709           if (!valCopy) {
7710             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7711             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7712             *values = valCopy;
7713           }
7714           for (i = 0; i < fdof; ++i) {
7715             PetscScalar fval = flip[i];
7716 
7717             for (k = 0; k < Ni; ++k) {
7718               valCopy[Ni * (foffset + i) + k] *= fval;
7719               valCopy[Ni * k + (foffset + i)] *= fval;
7720             }
7721           }
7722         }
7723         foffset += fdof;
7724       }
7725     }
7726   }
7727   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7728   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7729   if (NclC) {
7730     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7731     for (f = 0; f < PetscMax(1, Nf); ++f) {
7732       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7733       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7734     }
7735     for (f = 0; f < PetscMax(1, Nf); ++f) {
7736       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7737       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7738     }
7739     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7740     Ncl    = NclC;
7741     Ni     = NiC;
7742     points = pointsC;
7743     if (values) *values = valuesC;
7744   }
7745   /* 5) Calculate indices */
7746   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7747   if (Nf) {
7748     PetscInt  idxOff;
7749     PetscBool useFieldOffsets;
7750 
7751     if (outOffsets) {
7752       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7753     }
7754     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7755     if (useFieldOffsets) {
7756       for (p = 0; p < Ncl; ++p) {
7757         const PetscInt pnt = points[p * 2];
7758 
7759         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7760       }
7761     } else {
7762       for (p = 0; p < Ncl; ++p) {
7763         const PetscInt pnt = points[p * 2];
7764 
7765         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7766         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7767          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7768          * global section. */
7769         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7770       }
7771     }
7772   } else {
7773     PetscInt off = 0, idxOff;
7774 
7775     for (p = 0; p < Ncl; ++p) {
7776       const PetscInt  pnt  = points[p * 2];
7777       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7778 
7779       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7780       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7781        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7782       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7783     }
7784   }
7785   /* 6) Cleanup */
7786   for (f = 0; f < PetscMax(1, Nf); ++f) {
7787     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7788     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7789   }
7790   if (NclC) {
7791     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7792   } else {
7793     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7794   }
7795 
7796   if (numIndices) *numIndices = Ni;
7797   if (indices) *indices = idx;
7798   PetscFunctionReturn(PETSC_SUCCESS);
7799 }
7800 
7801 /*@C
7802   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7803 
7804   Not collective
7805 
7806   Input Parameters:
7807 + dm         - The `DM`
7808 . section    - The `PetscSection` describing the points (a local section)
7809 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7810 . point      - The point defining the closure
7811 - useClPerm  - Use the closure point permutation if available
7812 
7813   Output Parameters:
7814 + numIndices - The number of dof indices in the closure of point with the input sections
7815 . indices    - The dof indices
7816 . outOffsets - Array to write the field offsets into, or `NULL`
7817 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7818 
7819   Level: advanced
7820 
7821   Notes:
7822   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7823 
7824   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7825   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7826   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7827   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7828   indices (with the above semantics) are implied.
7829 
7830 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7831 @*/
7832 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7833 {
7834   PetscFunctionBegin;
7835   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7836   PetscValidPointer(indices, 7);
7837   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7838   PetscFunctionReturn(PETSC_SUCCESS);
7839 }
7840 
7841 /*@C
7842   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7843 
7844   Not collective
7845 
7846   Input Parameters:
7847 + dm - The `DM`
7848 . section - The section describing the layout in `v`, or `NULL` to use the default section
7849 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7850 . A - The matrix
7851 . point - The point in the `DM`
7852 . values - The array of values
7853 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7854 
7855   Level: intermediate
7856 
7857 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7858 @*/
7859 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7860 {
7861   DM_Plex           *mesh = (DM_Plex *)dm->data;
7862   PetscInt          *indices;
7863   PetscInt           numIndices;
7864   const PetscScalar *valuesOrig = values;
7865   PetscErrorCode     ierr;
7866 
7867   PetscFunctionBegin;
7868   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7869   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7870   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7871   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7872   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7873   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7874 
7875   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7876 
7877   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7878   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7879   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7880   if (ierr) {
7881     PetscMPIInt rank;
7882 
7883     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7884     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7885     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7886     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7887     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7888     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7889   }
7890   if (mesh->printFEM > 1) {
7891     PetscInt i;
7892     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7893     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7894     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7895   }
7896 
7897   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7898   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7899   PetscFunctionReturn(PETSC_SUCCESS);
7900 }
7901 
7902 /*@C
7903   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7904 
7905   Not collective
7906 
7907   Input Parameters:
7908 + dmRow - The `DM` for the row fields
7909 . sectionRow - The section describing the layout, or `NULL` to use the default section in `dmRow`
7910 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7911 . dmCol - The `DM` for the column fields
7912 . sectionCol - The section describing the layout, or `NULL` to use the default section in `dmCol`
7913 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7914 . A - The matrix
7915 . point - The point in the `DM`
7916 . values - The array of values
7917 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7918 
7919   Level: intermediate
7920 
7921 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7922 @*/
7923 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7924 {
7925   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7926   PetscInt          *indicesRow, *indicesCol;
7927   PetscInt           numIndicesRow, numIndicesCol;
7928   const PetscScalar *valuesOrig = values;
7929   PetscErrorCode     ierr;
7930 
7931   PetscFunctionBegin;
7932   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7933   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7934   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7935   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7936   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7937   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7938   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7939   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7940   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7941   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7942   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7943 
7944   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7945   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7946 
7947   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7948   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7949   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7950   if (ierr) {
7951     PetscMPIInt rank;
7952 
7953     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7954     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7955     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7956     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7957     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7958     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7959   }
7960 
7961   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7962   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7963   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7964   PetscFunctionReturn(PETSC_SUCCESS);
7965 }
7966 
7967 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7968 {
7969   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7970   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7971   PetscInt       *cpoints = NULL;
7972   PetscInt       *findices, *cindices;
7973   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7974   PetscInt        foffsets[32], coffsets[32];
7975   DMPolytopeType  ct;
7976   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7977   PetscErrorCode  ierr;
7978 
7979   PetscFunctionBegin;
7980   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7981   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7982   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7983   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7984   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7985   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7986   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7987   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7988   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7989   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7990   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7991   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7992   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7993   PetscCall(PetscArrayzero(foffsets, 32));
7994   PetscCall(PetscArrayzero(coffsets, 32));
7995   /* Column indices */
7996   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7997   maxFPoints = numCPoints;
7998   /* Compress out points not in the section */
7999   /*   TODO: Squeeze out points with 0 dof as well */
8000   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8001   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8002     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8003       cpoints[q * 2]     = cpoints[p];
8004       cpoints[q * 2 + 1] = cpoints[p + 1];
8005       ++q;
8006     }
8007   }
8008   numCPoints = q;
8009   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8010     PetscInt fdof;
8011 
8012     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8013     if (!dof) continue;
8014     for (f = 0; f < numFields; ++f) {
8015       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8016       coffsets[f + 1] += fdof;
8017     }
8018     numCIndices += dof;
8019   }
8020   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8021   /* Row indices */
8022   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8023   {
8024     DMPlexTransform tr;
8025     DMPolytopeType *rct;
8026     PetscInt       *rsize, *rcone, *rornt, Nt;
8027 
8028     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8029     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8030     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8031     numSubcells = rsize[Nt - 1];
8032     PetscCall(DMPlexTransformDestroy(&tr));
8033   }
8034   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8035   for (r = 0, q = 0; r < numSubcells; ++r) {
8036     /* TODO Map from coarse to fine cells */
8037     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8038     /* Compress out points not in the section */
8039     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8040     for (p = 0; p < numFPoints * 2; p += 2) {
8041       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8042         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8043         if (!dof) continue;
8044         for (s = 0; s < q; ++s)
8045           if (fpoints[p] == ftotpoints[s * 2]) break;
8046         if (s < q) continue;
8047         ftotpoints[q * 2]     = fpoints[p];
8048         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8049         ++q;
8050       }
8051     }
8052     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8053   }
8054   numFPoints = q;
8055   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8056     PetscInt fdof;
8057 
8058     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8059     if (!dof) continue;
8060     for (f = 0; f < numFields; ++f) {
8061       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8062       foffsets[f + 1] += fdof;
8063     }
8064     numFIndices += dof;
8065   }
8066   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8067 
8068   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8069   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8070   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8071   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8072   if (numFields) {
8073     const PetscInt **permsF[32] = {NULL};
8074     const PetscInt **permsC[32] = {NULL};
8075 
8076     for (f = 0; f < numFields; f++) {
8077       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8078       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8079     }
8080     for (p = 0; p < numFPoints; p++) {
8081       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8082       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8083     }
8084     for (p = 0; p < numCPoints; p++) {
8085       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8086       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8087     }
8088     for (f = 0; f < numFields; f++) {
8089       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8090       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8091     }
8092   } else {
8093     const PetscInt **permsF = NULL;
8094     const PetscInt **permsC = NULL;
8095 
8096     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8097     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8098     for (p = 0, off = 0; p < numFPoints; p++) {
8099       const PetscInt *perm = permsF ? permsF[p] : NULL;
8100 
8101       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8102       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8103     }
8104     for (p = 0, off = 0; p < numCPoints; p++) {
8105       const PetscInt *perm = permsC ? permsC[p] : NULL;
8106 
8107       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8108       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8109     }
8110     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8111     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8112   }
8113   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8114   /* TODO: flips */
8115   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8116   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8117   if (ierr) {
8118     PetscMPIInt rank;
8119 
8120     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8121     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8122     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8123     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8124     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8125   }
8126   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8127   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8128   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8129   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8130   PetscFunctionReturn(PETSC_SUCCESS);
8131 }
8132 
8133 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8134 {
8135   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8136   PetscInt       *cpoints = NULL;
8137   PetscInt        foffsets[32], coffsets[32];
8138   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8139   DMPolytopeType  ct;
8140   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8141 
8142   PetscFunctionBegin;
8143   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8144   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8145   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8146   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8147   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8148   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8149   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8150   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8151   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8152   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8153   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8154   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8155   PetscCall(PetscArrayzero(foffsets, 32));
8156   PetscCall(PetscArrayzero(coffsets, 32));
8157   /* Column indices */
8158   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8159   maxFPoints = numCPoints;
8160   /* Compress out points not in the section */
8161   /*   TODO: Squeeze out points with 0 dof as well */
8162   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8163   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8164     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8165       cpoints[q * 2]     = cpoints[p];
8166       cpoints[q * 2 + 1] = cpoints[p + 1];
8167       ++q;
8168     }
8169   }
8170   numCPoints = q;
8171   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8172     PetscInt fdof;
8173 
8174     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8175     if (!dof) continue;
8176     for (f = 0; f < numFields; ++f) {
8177       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8178       coffsets[f + 1] += fdof;
8179     }
8180     numCIndices += dof;
8181   }
8182   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8183   /* Row indices */
8184   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8185   {
8186     DMPlexTransform tr;
8187     DMPolytopeType *rct;
8188     PetscInt       *rsize, *rcone, *rornt, Nt;
8189 
8190     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8191     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8192     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8193     numSubcells = rsize[Nt - 1];
8194     PetscCall(DMPlexTransformDestroy(&tr));
8195   }
8196   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8197   for (r = 0, q = 0; r < numSubcells; ++r) {
8198     /* TODO Map from coarse to fine cells */
8199     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8200     /* Compress out points not in the section */
8201     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8202     for (p = 0; p < numFPoints * 2; p += 2) {
8203       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8204         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8205         if (!dof) continue;
8206         for (s = 0; s < q; ++s)
8207           if (fpoints[p] == ftotpoints[s * 2]) break;
8208         if (s < q) continue;
8209         ftotpoints[q * 2]     = fpoints[p];
8210         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8211         ++q;
8212       }
8213     }
8214     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8215   }
8216   numFPoints = q;
8217   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8218     PetscInt fdof;
8219 
8220     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8221     if (!dof) continue;
8222     for (f = 0; f < numFields; ++f) {
8223       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8224       foffsets[f + 1] += fdof;
8225     }
8226     numFIndices += dof;
8227   }
8228   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8229 
8230   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8231   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8232   if (numFields) {
8233     const PetscInt **permsF[32] = {NULL};
8234     const PetscInt **permsC[32] = {NULL};
8235 
8236     for (f = 0; f < numFields; f++) {
8237       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8238       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8239     }
8240     for (p = 0; p < numFPoints; p++) {
8241       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8242       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8243     }
8244     for (p = 0; p < numCPoints; p++) {
8245       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8246       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8247     }
8248     for (f = 0; f < numFields; f++) {
8249       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8250       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8251     }
8252   } else {
8253     const PetscInt **permsF = NULL;
8254     const PetscInt **permsC = NULL;
8255 
8256     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8257     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8258     for (p = 0, off = 0; p < numFPoints; p++) {
8259       const PetscInt *perm = permsF ? permsF[p] : NULL;
8260 
8261       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8262       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8263     }
8264     for (p = 0, off = 0; p < numCPoints; p++) {
8265       const PetscInt *perm = permsC ? permsC[p] : NULL;
8266 
8267       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8268       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8269     }
8270     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8271     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8272   }
8273   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8274   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8275   PetscFunctionReturn(PETSC_SUCCESS);
8276 }
8277 
8278 /*@C
8279   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8280 
8281   Input Parameter:
8282 . dm   - The `DMPLEX` object
8283 
8284   Output Parameter:
8285 . cellHeight - The height of a cell
8286 
8287   Level: developer
8288 
8289 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8290 @*/
8291 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8292 {
8293   DM_Plex *mesh = (DM_Plex *)dm->data;
8294 
8295   PetscFunctionBegin;
8296   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8297   PetscValidIntPointer(cellHeight, 2);
8298   *cellHeight = mesh->vtkCellHeight;
8299   PetscFunctionReturn(PETSC_SUCCESS);
8300 }
8301 
8302 /*@C
8303   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8304 
8305   Input Parameters:
8306 + dm   - The `DMPLEX` object
8307 - cellHeight - The height of a cell
8308 
8309   Level: developer
8310 
8311 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8312 @*/
8313 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8314 {
8315   DM_Plex *mesh = (DM_Plex *)dm->data;
8316 
8317   PetscFunctionBegin;
8318   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8319   mesh->vtkCellHeight = cellHeight;
8320   PetscFunctionReturn(PETSC_SUCCESS);
8321 }
8322 
8323 /*@
8324   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8325 
8326   Input Parameter:
8327 . dm - The `DMPLEX` object
8328 
8329   Output Parameters:
8330 + gcStart - The first ghost cell, or `NULL`
8331 - gcEnd   - The upper bound on ghost cells, or `NULL`
8332 
8333   Level: advanced
8334 
8335 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8336 @*/
8337 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8338 {
8339   DMLabel ctLabel;
8340 
8341   PetscFunctionBegin;
8342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8343   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8344   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8345   // Reset label for fast lookup
8346   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8347   PetscFunctionReturn(PETSC_SUCCESS);
8348 }
8349 
8350 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8351 {
8352   PetscSection section, globalSection;
8353   PetscInt    *numbers, p;
8354 
8355   PetscFunctionBegin;
8356   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8357   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8358   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8359   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8360   PetscCall(PetscSectionSetUp(section));
8361   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8362   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8363   for (p = pStart; p < pEnd; ++p) {
8364     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8365     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8366     else numbers[p - pStart] += shift;
8367   }
8368   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8369   if (globalSize) {
8370     PetscLayout layout;
8371     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8372     PetscCall(PetscLayoutGetSize(layout, globalSize));
8373     PetscCall(PetscLayoutDestroy(&layout));
8374   }
8375   PetscCall(PetscSectionDestroy(&section));
8376   PetscCall(PetscSectionDestroy(&globalSection));
8377   PetscFunctionReturn(PETSC_SUCCESS);
8378 }
8379 
8380 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8381 {
8382   PetscInt cellHeight, cStart, cEnd;
8383 
8384   PetscFunctionBegin;
8385   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8386   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8387   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8388   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8389   PetscFunctionReturn(PETSC_SUCCESS);
8390 }
8391 
8392 /*@
8393   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8394 
8395   Input Parameter:
8396 . dm   - The `DMPLEX` object
8397 
8398   Output Parameter:
8399 . globalCellNumbers - Global cell numbers for all cells on this process
8400 
8401   Level: developer
8402 
8403 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8404 @*/
8405 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8406 {
8407   DM_Plex *mesh = (DM_Plex *)dm->data;
8408 
8409   PetscFunctionBegin;
8410   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8411   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8412   *globalCellNumbers = mesh->globalCellNumbers;
8413   PetscFunctionReturn(PETSC_SUCCESS);
8414 }
8415 
8416 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8417 {
8418   PetscInt vStart, vEnd;
8419 
8420   PetscFunctionBegin;
8421   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8422   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8423   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8424   PetscFunctionReturn(PETSC_SUCCESS);
8425 }
8426 
8427 /*@
8428   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8429 
8430   Input Parameter:
8431 . dm   - The `DMPLEX` object
8432 
8433   Output Parameter:
8434 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8435 
8436   Level: developer
8437 
8438 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8439 @*/
8440 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8441 {
8442   DM_Plex *mesh = (DM_Plex *)dm->data;
8443 
8444   PetscFunctionBegin;
8445   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8446   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8447   *globalVertexNumbers = mesh->globalVertexNumbers;
8448   PetscFunctionReturn(PETSC_SUCCESS);
8449 }
8450 
8451 /*@
8452   DMPlexCreatePointNumbering - Create a global numbering for all points.
8453 
8454   Collective
8455 
8456   Input Parameter:
8457 . dm   - The `DMPLEX` object
8458 
8459   Output Parameter:
8460 . globalPointNumbers - Global numbers for all points on this process
8461 
8462   Level: developer
8463 
8464   Notes:
8465   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8466   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8467   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8468   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8469 
8470   The partitioned mesh is
8471 ```
8472  (2)--0--(3)--1--(4)    (1)--0--(2)
8473 ```
8474   and its global numbering is
8475 ```
8476   (3)--0--(4)--1--(5)--2--(6)
8477 ```
8478   Then the global numbering is provided as
8479 ```
8480 [0] Number of indices in set 5
8481 [0] 0 0
8482 [0] 1 1
8483 [0] 2 3
8484 [0] 3 4
8485 [0] 4 -6
8486 [1] Number of indices in set 3
8487 [1] 0 2
8488 [1] 1 5
8489 [1] 2 6
8490 ```
8491 
8492 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8493 @*/
8494 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8495 {
8496   IS        nums[4];
8497   PetscInt  depths[4], gdepths[4], starts[4];
8498   PetscInt  depth, d, shift = 0;
8499   PetscBool empty = PETSC_FALSE;
8500 
8501   PetscFunctionBegin;
8502   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8503   PetscCall(DMPlexGetDepth(dm, &depth));
8504   // For unstratified meshes use dim instead of depth
8505   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8506   // If any stratum is empty, we must mark all empty
8507   for (d = 0; d <= depth; ++d) {
8508     PetscInt end;
8509 
8510     depths[d] = depth - d;
8511     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8512     if (!(starts[d] - end)) empty = PETSC_TRUE;
8513   }
8514   if (empty)
8515     for (d = 0; d <= depth; ++d) {
8516       depths[d] = -1;
8517       starts[d] = -1;
8518     }
8519   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8520   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8521   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]);
8522   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8523   for (d = 0; d <= depth; ++d) {
8524     PetscInt pStart, pEnd, gsize;
8525 
8526     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8527     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8528     shift += gsize;
8529   }
8530   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8531   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8532   PetscFunctionReturn(PETSC_SUCCESS);
8533 }
8534 
8535 /*@
8536   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8537 
8538   Input Parameter:
8539 . dm - The `DMPLEX` object
8540 
8541   Output Parameter:
8542 . ranks - The rank field
8543 
8544   Options Database Key:
8545 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8546 
8547   Level: intermediate
8548 
8549 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8550 @*/
8551 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8552 {
8553   DM             rdm;
8554   PetscFE        fe;
8555   PetscScalar   *r;
8556   PetscMPIInt    rank;
8557   DMPolytopeType ct;
8558   PetscInt       dim, cStart, cEnd, c;
8559   PetscBool      simplex;
8560 
8561   PetscFunctionBeginUser;
8562   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8563   PetscValidPointer(ranks, 2);
8564   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8565   PetscCall(DMClone(dm, &rdm));
8566   PetscCall(DMGetDimension(rdm, &dim));
8567   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8568   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8569   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8570   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8571   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8572   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8573   PetscCall(PetscFEDestroy(&fe));
8574   PetscCall(DMCreateDS(rdm));
8575   PetscCall(DMCreateGlobalVector(rdm, ranks));
8576   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8577   PetscCall(VecGetArray(*ranks, &r));
8578   for (c = cStart; c < cEnd; ++c) {
8579     PetscScalar *lr;
8580 
8581     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8582     if (lr) *lr = rank;
8583   }
8584   PetscCall(VecRestoreArray(*ranks, &r));
8585   PetscCall(DMDestroy(&rdm));
8586   PetscFunctionReturn(PETSC_SUCCESS);
8587 }
8588 
8589 /*@
8590   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8591 
8592   Input Parameters:
8593 + dm    - The `DMPLEX`
8594 - label - The `DMLabel`
8595 
8596   Output Parameter:
8597 . val - The label value field
8598 
8599   Options Database Key:
8600 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8601 
8602   Level: intermediate
8603 
8604 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8605 @*/
8606 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8607 {
8608   DM           rdm;
8609   PetscFE      fe;
8610   PetscScalar *v;
8611   PetscInt     dim, cStart, cEnd, c;
8612 
8613   PetscFunctionBeginUser;
8614   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8615   PetscValidPointer(label, 2);
8616   PetscValidPointer(val, 3);
8617   PetscCall(DMClone(dm, &rdm));
8618   PetscCall(DMGetDimension(rdm, &dim));
8619   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8620   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8621   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8622   PetscCall(PetscFEDestroy(&fe));
8623   PetscCall(DMCreateDS(rdm));
8624   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8625   PetscCall(DMCreateGlobalVector(rdm, val));
8626   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8627   PetscCall(VecGetArray(*val, &v));
8628   for (c = cStart; c < cEnd; ++c) {
8629     PetscScalar *lv;
8630     PetscInt     cval;
8631 
8632     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8633     PetscCall(DMLabelGetValue(label, c, &cval));
8634     *lv = cval;
8635   }
8636   PetscCall(VecRestoreArray(*val, &v));
8637   PetscCall(DMDestroy(&rdm));
8638   PetscFunctionReturn(PETSC_SUCCESS);
8639 }
8640 
8641 /*@
8642   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8643 
8644   Input Parameter:
8645 . dm - The `DMPLEX` object
8646 
8647   Level: developer
8648 
8649   Notes:
8650   This is a useful diagnostic when creating meshes programmatically.
8651 
8652   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8653 
8654 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8655 @*/
8656 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8657 {
8658   PetscSection    coneSection, supportSection;
8659   const PetscInt *cone, *support;
8660   PetscInt        coneSize, c, supportSize, s;
8661   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8662   PetscBool       storagecheck = PETSC_TRUE;
8663 
8664   PetscFunctionBegin;
8665   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8666   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8667   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8668   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8669   /* Check that point p is found in the support of its cone points, and vice versa */
8670   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8671   for (p = pStart; p < pEnd; ++p) {
8672     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8673     PetscCall(DMPlexGetCone(dm, p, &cone));
8674     for (c = 0; c < coneSize; ++c) {
8675       PetscBool dup = PETSC_FALSE;
8676       PetscInt  d;
8677       for (d = c - 1; d >= 0; --d) {
8678         if (cone[c] == cone[d]) {
8679           dup = PETSC_TRUE;
8680           break;
8681         }
8682       }
8683       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8684       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8685       for (s = 0; s < supportSize; ++s) {
8686         if (support[s] == p) break;
8687       }
8688       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8689         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8690         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8691         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8692         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8693         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8694         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8695         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]);
8696         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8697       }
8698     }
8699     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8700     if (p != pp) {
8701       storagecheck = PETSC_FALSE;
8702       continue;
8703     }
8704     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8705     PetscCall(DMPlexGetSupport(dm, p, &support));
8706     for (s = 0; s < supportSize; ++s) {
8707       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8708       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8709       for (c = 0; c < coneSize; ++c) {
8710         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8711         if (cone[c] != pp) {
8712           c = 0;
8713           break;
8714         }
8715         if (cone[c] == p) break;
8716       }
8717       if (c >= coneSize) {
8718         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8719         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8720         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8721         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8722         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8723         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8724         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8725       }
8726     }
8727   }
8728   if (storagecheck) {
8729     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8730     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8731     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8732   }
8733   PetscFunctionReturn(PETSC_SUCCESS);
8734 }
8735 
8736 /*
8737   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.
8738 */
8739 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8740 {
8741   DMPolytopeType  cct;
8742   PetscInt        ptpoints[4];
8743   const PetscInt *cone, *ccone, *ptcone;
8744   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8745 
8746   PetscFunctionBegin;
8747   *unsplit = 0;
8748   switch (ct) {
8749   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8750     ptpoints[npt++] = c;
8751     break;
8752   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8753     PetscCall(DMPlexGetCone(dm, c, &cone));
8754     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8755     for (cp = 0; cp < coneSize; ++cp) {
8756       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8757       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8758     }
8759     break;
8760   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8761   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8762     PetscCall(DMPlexGetCone(dm, c, &cone));
8763     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8764     for (cp = 0; cp < coneSize; ++cp) {
8765       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8766       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8767       for (ccp = 0; ccp < cconeSize; ++ccp) {
8768         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8769         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8770           PetscInt p;
8771           for (p = 0; p < npt; ++p)
8772             if (ptpoints[p] == ccone[ccp]) break;
8773           if (p == npt) ptpoints[npt++] = ccone[ccp];
8774         }
8775       }
8776     }
8777     break;
8778   default:
8779     break;
8780   }
8781   for (pt = 0; pt < npt; ++pt) {
8782     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8783     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8784   }
8785   PetscFunctionReturn(PETSC_SUCCESS);
8786 }
8787 
8788 /*@
8789   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8790 
8791   Input Parameters:
8792 + dm - The `DMPLEX` object
8793 - cellHeight - Normally 0
8794 
8795   Level: developer
8796 
8797   Notes:
8798   This is a useful diagnostic when creating meshes programmatically.
8799   Currently applicable only to homogeneous simplex or tensor meshes.
8800 
8801   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8802 
8803 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8804 @*/
8805 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8806 {
8807   DMPlexInterpolatedFlag interp;
8808   DMPolytopeType         ct;
8809   PetscInt               vStart, vEnd, cStart, cEnd, c;
8810 
8811   PetscFunctionBegin;
8812   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8813   PetscCall(DMPlexIsInterpolated(dm, &interp));
8814   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8815   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8816   for (c = cStart; c < cEnd; ++c) {
8817     PetscInt *closure = NULL;
8818     PetscInt  coneSize, closureSize, cl, Nv = 0;
8819 
8820     PetscCall(DMPlexGetCellType(dm, c, &ct));
8821     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8822     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8823     if (interp == DMPLEX_INTERPOLATED_FULL) {
8824       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8825       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));
8826     }
8827     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8828     for (cl = 0; cl < closureSize * 2; cl += 2) {
8829       const PetscInt p = closure[cl];
8830       if ((p >= vStart) && (p < vEnd)) ++Nv;
8831     }
8832     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8833     /* Special Case: Tensor faces with identified vertices */
8834     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8835       PetscInt unsplit;
8836 
8837       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8838       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8839     }
8840     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));
8841   }
8842   PetscFunctionReturn(PETSC_SUCCESS);
8843 }
8844 
8845 /*@
8846   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8847 
8848   Collective
8849 
8850   Input Parameters:
8851 + dm - The `DMPLEX` object
8852 - cellHeight - Normally 0
8853 
8854   Level: developer
8855 
8856   Notes:
8857   This is a useful diagnostic when creating meshes programmatically.
8858   This routine is only relevant for meshes that are fully interpolated across all ranks.
8859   It will error out if a partially interpolated mesh is given on some rank.
8860   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8861 
8862   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8863 
8864 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8865 @*/
8866 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8867 {
8868   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8869   DMPlexInterpolatedFlag interpEnum;
8870 
8871   PetscFunctionBegin;
8872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8873   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8874   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8875   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8876     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8877     PetscFunctionReturn(PETSC_SUCCESS);
8878   }
8879 
8880   PetscCall(DMGetDimension(dm, &dim));
8881   PetscCall(DMPlexGetDepth(dm, &depth));
8882   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8883   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8884     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8885     for (c = cStart; c < cEnd; ++c) {
8886       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8887       const DMPolytopeType *faceTypes;
8888       DMPolytopeType        ct;
8889       PetscInt              numFaces, coneSize, f;
8890       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8891 
8892       PetscCall(DMPlexGetCellType(dm, c, &ct));
8893       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8894       if (unsplit) continue;
8895       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8896       PetscCall(DMPlexGetCone(dm, c, &cone));
8897       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8898       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8899       for (cl = 0; cl < closureSize * 2; cl += 2) {
8900         const PetscInt p = closure[cl];
8901         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8902       }
8903       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8904       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);
8905       for (f = 0; f < numFaces; ++f) {
8906         DMPolytopeType fct;
8907         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8908 
8909         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8910         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8911         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8912           const PetscInt p = fclosure[cl];
8913           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8914         }
8915         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]);
8916         for (v = 0; v < fnumCorners; ++v) {
8917           if (fclosure[v] != faces[fOff + v]) {
8918             PetscInt v1;
8919 
8920             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8921             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8922             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8923             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8924             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8925             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]);
8926           }
8927         }
8928         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8929         fOff += faceSizes[f];
8930       }
8931       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8932       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8933     }
8934   }
8935   PetscFunctionReturn(PETSC_SUCCESS);
8936 }
8937 
8938 /*@
8939   DMPlexCheckGeometry - Check the geometry of mesh cells
8940 
8941   Input Parameter:
8942 . dm - The `DMPLEX` object
8943 
8944   Level: developer
8945 
8946   Notes:
8947   This is a useful diagnostic when creating meshes programmatically.
8948 
8949   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8950 
8951 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8952 @*/
8953 PetscErrorCode DMPlexCheckGeometry(DM dm)
8954 {
8955   Vec       coordinates;
8956   PetscReal detJ, J[9], refVol = 1.0;
8957   PetscReal vol;
8958   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8959 
8960   PetscFunctionBegin;
8961   PetscCall(DMGetDimension(dm, &dim));
8962   PetscCall(DMGetCoordinateDim(dm, &dE));
8963   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
8964   PetscCall(DMPlexGetDepth(dm, &depth));
8965   for (d = 0; d < dim; ++d) refVol *= 2.0;
8966   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8967   /* Make sure local coordinates are created, because that step is collective */
8968   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8969   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
8970   for (c = cStart; c < cEnd; ++c) {
8971     DMPolytopeType ct;
8972     PetscInt       unsplit;
8973     PetscBool      ignoreZeroVol = PETSC_FALSE;
8974 
8975     PetscCall(DMPlexGetCellType(dm, c, &ct));
8976     switch (ct) {
8977     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8978     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8979     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8980       ignoreZeroVol = PETSC_TRUE;
8981       break;
8982     default:
8983       break;
8984     }
8985     switch (ct) {
8986     case DM_POLYTOPE_TRI_PRISM:
8987     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8988     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8989     case DM_POLYTOPE_PYRAMID:
8990       continue;
8991     default:
8992       break;
8993     }
8994     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8995     if (unsplit) continue;
8996     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8997     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);
8998     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8999     /* This should work with periodicity since DG coordinates should be used */
9000     if (depth > 1) {
9001       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9002       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);
9003       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9004     }
9005   }
9006   PetscFunctionReturn(PETSC_SUCCESS);
9007 }
9008 
9009 /*@
9010   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9011 
9012   Collective
9013 
9014   Input Parameters:
9015 + dm - The `DMPLEX` object
9016 . pointSF - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9017 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9018 
9019   Level: developer
9020 
9021   Notes:
9022   This is mainly intended for debugging/testing purposes.
9023 
9024   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9025 
9026   Extra roots can come from priodic cuts, where additional points appear on the boundary
9027 
9028 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9029 @*/
9030 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9031 {
9032   PetscInt           l, nleaves, nroots, overlap;
9033   const PetscInt    *locals;
9034   const PetscSFNode *remotes;
9035   PetscBool          distributed;
9036   MPI_Comm           comm;
9037   PetscMPIInt        rank;
9038 
9039   PetscFunctionBegin;
9040   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9041   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9042   else pointSF = dm->sf;
9043   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9044   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9045   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9046   {
9047     PetscMPIInt mpiFlag;
9048 
9049     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9050     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9051   }
9052   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9053   PetscCall(DMPlexIsDistributed(dm, &distributed));
9054   if (!distributed) {
9055     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);
9056     PetscFunctionReturn(PETSC_SUCCESS);
9057   }
9058   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);
9059   PetscCall(DMPlexGetOverlap(dm, &overlap));
9060 
9061   /* Check SF graph is compatible with DMPlex chart */
9062   {
9063     PetscInt pStart, pEnd, maxLeaf;
9064 
9065     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9066     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9067     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9068     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9069   }
9070 
9071   /* Check Point SF has no local points referenced */
9072   for (l = 0; l < nleaves; l++) {
9073     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);
9074   }
9075 
9076   /* Check there are no cells in interface */
9077   if (!overlap) {
9078     PetscInt cellHeight, cStart, cEnd;
9079 
9080     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9081     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9082     for (l = 0; l < nleaves; ++l) {
9083       const PetscInt point = locals ? locals[l] : l;
9084 
9085       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9086     }
9087   }
9088 
9089   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9090   {
9091     const PetscInt *rootdegree;
9092 
9093     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9094     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9095     for (l = 0; l < nleaves; ++l) {
9096       const PetscInt  point = locals ? locals[l] : l;
9097       const PetscInt *cone;
9098       PetscInt        coneSize, c, idx;
9099 
9100       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9101       PetscCall(DMPlexGetCone(dm, point, &cone));
9102       for (c = 0; c < coneSize; ++c) {
9103         if (!rootdegree[cone[c]]) {
9104           if (locals) {
9105             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9106           } else {
9107             idx = (cone[c] < nleaves) ? cone[c] : -1;
9108           }
9109           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9110         }
9111       }
9112     }
9113   }
9114   PetscFunctionReturn(PETSC_SUCCESS);
9115 }
9116 
9117 /*@
9118   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9119 
9120   Input Parameter:
9121 . dm - The `DMPLEX` object
9122 
9123   Level: developer
9124 
9125   Notes:
9126   This is a useful diagnostic when creating meshes programmatically.
9127 
9128   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9129 
9130   Currently does not include `DMPlexCheckCellShape()`.
9131 
9132 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9133 @*/
9134 PetscErrorCode DMPlexCheck(DM dm)
9135 {
9136   PetscInt cellHeight;
9137 
9138   PetscFunctionBegin;
9139   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9140   PetscCall(DMPlexCheckSymmetry(dm));
9141   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9142   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9143   PetscCall(DMPlexCheckGeometry(dm));
9144   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9145   PetscCall(DMPlexCheckInterfaceCones(dm));
9146   PetscFunctionReturn(PETSC_SUCCESS);
9147 }
9148 
9149 typedef struct cell_stats {
9150   PetscReal min, max, sum, squaresum;
9151   PetscInt  count;
9152 } cell_stats_t;
9153 
9154 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9155 {
9156   PetscInt i, N = *len;
9157 
9158   for (i = 0; i < N; i++) {
9159     cell_stats_t *A = (cell_stats_t *)a;
9160     cell_stats_t *B = (cell_stats_t *)b;
9161 
9162     B->min = PetscMin(A->min, B->min);
9163     B->max = PetscMax(A->max, B->max);
9164     B->sum += A->sum;
9165     B->squaresum += A->squaresum;
9166     B->count += A->count;
9167   }
9168 }
9169 
9170 /*@
9171   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9172 
9173   Collective
9174 
9175   Input Parameters:
9176 + dm        - The `DMPLEX` object
9177 . output    - If true, statistics will be displayed on `stdout`
9178 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9179 
9180   Level: developer
9181 
9182   Notes:
9183   This is mainly intended for debugging/testing purposes.
9184 
9185   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9186 
9187 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9188 @*/
9189 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9190 {
9191   DM           dmCoarse;
9192   cell_stats_t stats, globalStats;
9193   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9194   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9195   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9196   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9197   PetscMPIInt  rank, size;
9198 
9199   PetscFunctionBegin;
9200   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9201   stats.min = PETSC_MAX_REAL;
9202   stats.max = PETSC_MIN_REAL;
9203   stats.sum = stats.squaresum = 0.;
9204   stats.count                 = 0;
9205 
9206   PetscCallMPI(MPI_Comm_size(comm, &size));
9207   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9208   PetscCall(DMGetCoordinateDim(dm, &cdim));
9209   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9210   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9211   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9212   for (c = cStart; c < cEnd; c++) {
9213     PetscInt  i;
9214     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9215 
9216     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9217     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9218     for (i = 0; i < PetscSqr(cdim); ++i) {
9219       frobJ += J[i] * J[i];
9220       frobInvJ += invJ[i] * invJ[i];
9221     }
9222     cond2 = frobJ * frobInvJ;
9223     cond  = PetscSqrtReal(cond2);
9224 
9225     stats.min = PetscMin(stats.min, cond);
9226     stats.max = PetscMax(stats.max, cond);
9227     stats.sum += cond;
9228     stats.squaresum += cond2;
9229     stats.count++;
9230     if (output && cond > limit) {
9231       PetscSection coordSection;
9232       Vec          coordsLocal;
9233       PetscScalar *coords = NULL;
9234       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9235 
9236       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9237       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9238       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9239       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9240       for (i = 0; i < Nv / cdim; ++i) {
9241         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9242         for (d = 0; d < cdim; ++d) {
9243           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9244           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9245         }
9246         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9247       }
9248       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9249       for (cl = 0; cl < clSize * 2; cl += 2) {
9250         const PetscInt edge = closure[cl];
9251 
9252         if ((edge >= eStart) && (edge < eEnd)) {
9253           PetscReal len;
9254 
9255           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9256           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9257         }
9258       }
9259       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9260       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9261     }
9262   }
9263   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9264 
9265   if (size > 1) {
9266     PetscMPIInt  blockLengths[2] = {4, 1};
9267     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9268     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9269     MPI_Op       statReduce;
9270 
9271     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9272     PetscCallMPI(MPI_Type_commit(&statType));
9273     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9274     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9275     PetscCallMPI(MPI_Op_free(&statReduce));
9276     PetscCallMPI(MPI_Type_free(&statType));
9277   } else {
9278     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9279   }
9280   if (rank == 0) {
9281     count = globalStats.count;
9282     min   = globalStats.min;
9283     max   = globalStats.max;
9284     mean  = globalStats.sum / globalStats.count;
9285     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9286   }
9287 
9288   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));
9289   PetscCall(PetscFree2(J, invJ));
9290 
9291   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9292   if (dmCoarse) {
9293     PetscBool isplex;
9294 
9295     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9296     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9297   }
9298   PetscFunctionReturn(PETSC_SUCCESS);
9299 }
9300 
9301 /*@
9302   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9303   orthogonal quality below given tolerance.
9304 
9305   Collective
9306 
9307   Input Parameters:
9308 + dm   - The `DMPLEX` object
9309 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9310 - atol - [0, 1] Absolute tolerance for tagging cells.
9311 
9312   Output Parameters:
9313 + OrthQual      - `Vec` containing orthogonal quality per cell
9314 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9315 
9316   Options Database Keys:
9317 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9318 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9319 
9320   Level: intermediate
9321 
9322   Notes:
9323   Orthogonal quality is given by the following formula:
9324 
9325   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9326 
9327   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
9328   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9329   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9330   calculating the cosine of the angle between these vectors.
9331 
9332   Orthogonal quality ranges from 1 (best) to 0 (worst).
9333 
9334   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9335   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9336 
9337   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9338 
9339 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9340 @*/
9341 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9342 {
9343   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9344   PetscInt              *idx;
9345   PetscScalar           *oqVals;
9346   const PetscScalar     *cellGeomArr, *faceGeomArr;
9347   PetscReal             *ci, *fi, *Ai;
9348   MPI_Comm               comm;
9349   Vec                    cellgeom, facegeom;
9350   DM                     dmFace, dmCell;
9351   IS                     glob;
9352   ISLocalToGlobalMapping ltog;
9353   PetscViewer            vwr;
9354 
9355   PetscFunctionBegin;
9356   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9357   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9358   PetscValidPointer(OrthQual, 4);
9359   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9360   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9361   PetscCall(DMGetDimension(dm, &nc));
9362   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9363   {
9364     DMPlexInterpolatedFlag interpFlag;
9365 
9366     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9367     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9368       PetscMPIInt rank;
9369 
9370       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9371       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9372     }
9373   }
9374   if (OrthQualLabel) {
9375     PetscValidPointer(OrthQualLabel, 5);
9376     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9377     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9378   } else {
9379     *OrthQualLabel = NULL;
9380   }
9381   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9382   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9383   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9384   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9385   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9386   PetscCall(VecCreate(comm, OrthQual));
9387   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9388   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9389   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9390   PetscCall(VecSetUp(*OrthQual));
9391   PetscCall(ISDestroy(&glob));
9392   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9393   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9394   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9395   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9396   PetscCall(VecGetDM(cellgeom, &dmCell));
9397   PetscCall(VecGetDM(facegeom, &dmFace));
9398   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9399   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9400     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9401     PetscInt         cellarr[2], *adj = NULL;
9402     PetscScalar     *cArr, *fArr;
9403     PetscReal        minvalc = 1.0, minvalf = 1.0;
9404     PetscFVCellGeom *cg;
9405 
9406     idx[cellIter] = cell - cStart;
9407     cellarr[0]    = cell;
9408     /* Make indexing into cellGeom easier */
9409     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9410     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9411     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9412     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9413     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9414       PetscInt         i;
9415       const PetscInt   neigh  = adj[cellneigh];
9416       PetscReal        normci = 0, normfi = 0, normai = 0;
9417       PetscFVCellGeom *cgneigh;
9418       PetscFVFaceGeom *fg;
9419 
9420       /* Don't count ourselves in the neighbor list */
9421       if (neigh == cell) continue;
9422       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9423       cellarr[1] = neigh;
9424       {
9425         PetscInt        numcovpts;
9426         const PetscInt *covpts;
9427 
9428         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9429         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9430         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9431       }
9432 
9433       /* Compute c_i, f_i and their norms */
9434       for (i = 0; i < nc; i++) {
9435         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9436         fi[i] = fg->centroid[i] - cg->centroid[i];
9437         Ai[i] = fg->normal[i];
9438         normci += PetscPowReal(ci[i], 2);
9439         normfi += PetscPowReal(fi[i], 2);
9440         normai += PetscPowReal(Ai[i], 2);
9441       }
9442       normci = PetscSqrtReal(normci);
9443       normfi = PetscSqrtReal(normfi);
9444       normai = PetscSqrtReal(normai);
9445 
9446       /* Normalize and compute for each face-cell-normal pair */
9447       for (i = 0; i < nc; i++) {
9448         ci[i] = ci[i] / normci;
9449         fi[i] = fi[i] / normfi;
9450         Ai[i] = Ai[i] / normai;
9451         /* PetscAbs because I don't know if normals are guaranteed to point out */
9452         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9453         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9454       }
9455       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9456       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9457     }
9458     PetscCall(PetscFree(adj));
9459     PetscCall(PetscFree2(cArr, fArr));
9460     /* Defer to cell if they're equal */
9461     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9462     if (OrthQualLabel) {
9463       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9464     }
9465   }
9466   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9467   PetscCall(VecAssemblyBegin(*OrthQual));
9468   PetscCall(VecAssemblyEnd(*OrthQual));
9469   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9470   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9471   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9472   if (OrthQualLabel) {
9473     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9474   }
9475   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9476   PetscCall(PetscViewerDestroy(&vwr));
9477   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9478   PetscFunctionReturn(PETSC_SUCCESS);
9479 }
9480 
9481 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9482  * interpolator construction */
9483 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9484 {
9485   PetscSection section, newSection, gsection;
9486   PetscSF      sf;
9487   PetscBool    hasConstraints, ghasConstraints;
9488 
9489   PetscFunctionBegin;
9490   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9491   PetscValidPointer(odm, 2);
9492   PetscCall(DMGetLocalSection(dm, &section));
9493   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9494   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9495   if (!ghasConstraints) {
9496     PetscCall(PetscObjectReference((PetscObject)dm));
9497     *odm = dm;
9498     PetscFunctionReturn(PETSC_SUCCESS);
9499   }
9500   PetscCall(DMClone(dm, odm));
9501   PetscCall(DMCopyFields(dm, *odm));
9502   PetscCall(DMGetLocalSection(*odm, &newSection));
9503   PetscCall(DMGetPointSF(*odm, &sf));
9504   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9505   PetscCall(DMSetGlobalSection(*odm, gsection));
9506   PetscCall(PetscSectionDestroy(&gsection));
9507   PetscFunctionReturn(PETSC_SUCCESS);
9508 }
9509 
9510 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9511 {
9512   DM        dmco, dmfo;
9513   Mat       interpo;
9514   Vec       rscale;
9515   Vec       cglobalo, clocal;
9516   Vec       fglobal, fglobalo, flocal;
9517   PetscBool regular;
9518 
9519   PetscFunctionBegin;
9520   PetscCall(DMGetFullDM(dmc, &dmco));
9521   PetscCall(DMGetFullDM(dmf, &dmfo));
9522   PetscCall(DMSetCoarseDM(dmfo, dmco));
9523   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9524   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9525   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9526   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9527   PetscCall(DMCreateLocalVector(dmc, &clocal));
9528   PetscCall(VecSet(cglobalo, 0.));
9529   PetscCall(VecSet(clocal, 0.));
9530   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9531   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9532   PetscCall(DMCreateLocalVector(dmf, &flocal));
9533   PetscCall(VecSet(fglobal, 0.));
9534   PetscCall(VecSet(fglobalo, 0.));
9535   PetscCall(VecSet(flocal, 0.));
9536   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9537   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9538   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9539   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9540   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9541   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9542   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9543   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9544   *shift = fglobal;
9545   PetscCall(VecDestroy(&flocal));
9546   PetscCall(VecDestroy(&fglobalo));
9547   PetscCall(VecDestroy(&clocal));
9548   PetscCall(VecDestroy(&cglobalo));
9549   PetscCall(VecDestroy(&rscale));
9550   PetscCall(MatDestroy(&interpo));
9551   PetscCall(DMDestroy(&dmfo));
9552   PetscCall(DMDestroy(&dmco));
9553   PetscFunctionReturn(PETSC_SUCCESS);
9554 }
9555 
9556 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9557 {
9558   PetscObject shifto;
9559   Vec         shift;
9560 
9561   PetscFunctionBegin;
9562   if (!interp) {
9563     Vec rscale;
9564 
9565     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9566     PetscCall(VecDestroy(&rscale));
9567   } else {
9568     PetscCall(PetscObjectReference((PetscObject)interp));
9569   }
9570   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9571   if (!shifto) {
9572     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9573     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9574     shifto = (PetscObject)shift;
9575     PetscCall(VecDestroy(&shift));
9576   }
9577   shift = (Vec)shifto;
9578   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9579   PetscCall(VecAXPY(fineSol, 1.0, shift));
9580   PetscCall(MatDestroy(&interp));
9581   PetscFunctionReturn(PETSC_SUCCESS);
9582 }
9583 
9584 /* Pointwise interpolation
9585      Just code FEM for now
9586      u^f = I u^c
9587      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9588      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9589      I_{ij} = psi^f_i phi^c_j
9590 */
9591 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9592 {
9593   PetscSection gsc, gsf;
9594   PetscInt     m, n;
9595   void        *ctx;
9596   DM           cdm;
9597   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9598 
9599   PetscFunctionBegin;
9600   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9601   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9602   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9603   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9604 
9605   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9606   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9607   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9608   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9609   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9610 
9611   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9612   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9613   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9614   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9615   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9616   if (scaling) {
9617     /* Use naive scaling */
9618     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9619   }
9620   PetscFunctionReturn(PETSC_SUCCESS);
9621 }
9622 
9623 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9624 {
9625   VecScatter ctx;
9626 
9627   PetscFunctionBegin;
9628   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9629   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9630   PetscCall(VecScatterDestroy(&ctx));
9631   PetscFunctionReturn(PETSC_SUCCESS);
9632 }
9633 
9634 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[])
9635 {
9636   const PetscInt Nc = uOff[1] - uOff[0];
9637   PetscInt       c;
9638   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9639 }
9640 
9641 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9642 {
9643   DM           dmc;
9644   PetscDS      ds;
9645   Vec          ones, locmass;
9646   IS           cellIS;
9647   PetscFormKey key;
9648   PetscInt     depth;
9649 
9650   PetscFunctionBegin;
9651   PetscCall(DMClone(dm, &dmc));
9652   PetscCall(DMCopyDisc(dm, dmc));
9653   PetscCall(DMGetDS(dmc, &ds));
9654   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9655   PetscCall(DMCreateGlobalVector(dmc, mass));
9656   PetscCall(DMGetLocalVector(dmc, &ones));
9657   PetscCall(DMGetLocalVector(dmc, &locmass));
9658   PetscCall(DMPlexGetDepth(dmc, &depth));
9659   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9660   PetscCall(VecSet(locmass, 0.0));
9661   PetscCall(VecSet(ones, 1.0));
9662   key.label = NULL;
9663   key.value = 0;
9664   key.field = 0;
9665   key.part  = 0;
9666   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9667   PetscCall(ISDestroy(&cellIS));
9668   PetscCall(VecSet(*mass, 0.0));
9669   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9670   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9671   PetscCall(DMRestoreLocalVector(dmc, &ones));
9672   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9673   PetscCall(DMDestroy(&dmc));
9674   PetscFunctionReturn(PETSC_SUCCESS);
9675 }
9676 
9677 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9678 {
9679   PetscSection gsc, gsf;
9680   PetscInt     m, n;
9681   void        *ctx;
9682   DM           cdm;
9683   PetscBool    regular;
9684 
9685   PetscFunctionBegin;
9686   if (dmFine == dmCoarse) {
9687     DM            dmc;
9688     PetscDS       ds;
9689     PetscWeakForm wf;
9690     Vec           u;
9691     IS            cellIS;
9692     PetscFormKey  key;
9693     PetscInt      depth;
9694 
9695     PetscCall(DMClone(dmFine, &dmc));
9696     PetscCall(DMCopyDisc(dmFine, dmc));
9697     PetscCall(DMGetDS(dmc, &ds));
9698     PetscCall(PetscDSGetWeakForm(ds, &wf));
9699     PetscCall(PetscWeakFormClear(wf));
9700     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9701     PetscCall(DMCreateMatrix(dmc, mass));
9702     PetscCall(DMGetLocalVector(dmc, &u));
9703     PetscCall(DMPlexGetDepth(dmc, &depth));
9704     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9705     PetscCall(MatZeroEntries(*mass));
9706     key.label = NULL;
9707     key.value = 0;
9708     key.field = 0;
9709     key.part  = 0;
9710     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9711     PetscCall(ISDestroy(&cellIS));
9712     PetscCall(DMRestoreLocalVector(dmc, &u));
9713     PetscCall(DMDestroy(&dmc));
9714   } else {
9715     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9716     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9717     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9718     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9719 
9720     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9721     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9722     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9723     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9724 
9725     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9726     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9727     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9728     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9729   }
9730   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9731   PetscFunctionReturn(PETSC_SUCCESS);
9732 }
9733 
9734 /*@
9735   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9736 
9737   Input Parameter:
9738 . dm - The `DMPLEX` object
9739 
9740   Output Parameter:
9741 . regular - The flag
9742 
9743   Level: intermediate
9744 
9745 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9746 @*/
9747 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9748 {
9749   PetscFunctionBegin;
9750   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9751   PetscValidBoolPointer(regular, 2);
9752   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9753   PetscFunctionReturn(PETSC_SUCCESS);
9754 }
9755 
9756 /*@
9757   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9758 
9759   Input Parameters:
9760 + dm - The `DMPLEX` object
9761 - regular - The flag
9762 
9763   Level: intermediate
9764 
9765 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9766 @*/
9767 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9768 {
9769   PetscFunctionBegin;
9770   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9771   ((DM_Plex *)dm->data)->regularRefinement = regular;
9772   PetscFunctionReturn(PETSC_SUCCESS);
9773 }
9774 
9775 /*@
9776   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9777   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9778 
9779   Not Collective
9780 
9781   Input Parameter:
9782 . dm - The `DMPLEX` object
9783 
9784   Output Parameters:
9785 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9786 - anchorIS - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9787 
9788   Level: intermediate
9789 
9790 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9791 @*/
9792 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9793 {
9794   DM_Plex *plex = (DM_Plex *)dm->data;
9795 
9796   PetscFunctionBegin;
9797   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9798   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9799   if (anchorSection) *anchorSection = plex->anchorSection;
9800   if (anchorIS) *anchorIS = plex->anchorIS;
9801   PetscFunctionReturn(PETSC_SUCCESS);
9802 }
9803 
9804 /*@
9805   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9806   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9807   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9808 
9809   Collective
9810 
9811   Input Parameters:
9812 + dm - The `DMPLEX` object
9813 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9814                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9815 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9816 
9817   Level: intermediate
9818 
9819   Notes:
9820   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9821   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9822 
9823   The reference counts of `anchorSection` and `anchorIS` are incremented.
9824 
9825 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9826 @*/
9827 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9828 {
9829   DM_Plex    *plex = (DM_Plex *)dm->data;
9830   PetscMPIInt result;
9831 
9832   PetscFunctionBegin;
9833   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9834   if (anchorSection) {
9835     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9836     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9837     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9838   }
9839   if (anchorIS) {
9840     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9841     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9842     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9843   }
9844 
9845   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9846   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9847   plex->anchorSection = anchorSection;
9848 
9849   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9850   PetscCall(ISDestroy(&plex->anchorIS));
9851   plex->anchorIS = anchorIS;
9852 
9853   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9854     PetscInt        size, a, pStart, pEnd;
9855     const PetscInt *anchors;
9856 
9857     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9858     PetscCall(ISGetLocalSize(anchorIS, &size));
9859     PetscCall(ISGetIndices(anchorIS, &anchors));
9860     for (a = 0; a < size; a++) {
9861       PetscInt p;
9862 
9863       p = anchors[a];
9864       if (p >= pStart && p < pEnd) {
9865         PetscInt dof;
9866 
9867         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9868         if (dof) {
9869           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9870           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9871         }
9872       }
9873     }
9874     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9875   }
9876   /* reset the generic constraints */
9877   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9878   PetscFunctionReturn(PETSC_SUCCESS);
9879 }
9880 
9881 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9882 {
9883   PetscSection anchorSection;
9884   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9885 
9886   PetscFunctionBegin;
9887   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9888   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9889   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9890   PetscCall(PetscSectionGetNumFields(section, &numFields));
9891   if (numFields) {
9892     PetscInt f;
9893     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9894 
9895     for (f = 0; f < numFields; f++) {
9896       PetscInt numComp;
9897 
9898       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9899       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9900     }
9901   }
9902   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9903   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9904   pStart = PetscMax(pStart, sStart);
9905   pEnd   = PetscMin(pEnd, sEnd);
9906   pEnd   = PetscMax(pStart, pEnd);
9907   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9908   for (p = pStart; p < pEnd; p++) {
9909     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9910     if (dof) {
9911       PetscCall(PetscSectionGetDof(section, p, &dof));
9912       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9913       for (f = 0; f < numFields; f++) {
9914         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9915         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9916       }
9917     }
9918   }
9919   PetscCall(PetscSectionSetUp(*cSec));
9920   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9921   PetscFunctionReturn(PETSC_SUCCESS);
9922 }
9923 
9924 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9925 {
9926   PetscSection    aSec;
9927   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9928   const PetscInt *anchors;
9929   PetscInt        numFields, f;
9930   IS              aIS;
9931   MatType         mtype;
9932   PetscBool       iscuda, iskokkos;
9933 
9934   PetscFunctionBegin;
9935   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9936   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9937   PetscCall(PetscSectionGetStorageSize(section, &n));
9938   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9939   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9940   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9941   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9942   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9943   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9944   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9945   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9946   else mtype = MATSEQAIJ;
9947   PetscCall(MatSetType(*cMat, mtype));
9948   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9949   PetscCall(ISGetIndices(aIS, &anchors));
9950   /* cSec will be a subset of aSec and section */
9951   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9952   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9953   PetscCall(PetscMalloc1(m + 1, &i));
9954   i[0] = 0;
9955   PetscCall(PetscSectionGetNumFields(section, &numFields));
9956   for (p = pStart; p < pEnd; p++) {
9957     PetscInt rDof, rOff, r;
9958 
9959     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9960     if (!rDof) continue;
9961     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9962     if (numFields) {
9963       for (f = 0; f < numFields; f++) {
9964         annz = 0;
9965         for (r = 0; r < rDof; r++) {
9966           a = anchors[rOff + r];
9967           if (a < sStart || a >= sEnd) continue;
9968           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9969           annz += aDof;
9970         }
9971         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9972         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9973         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9974       }
9975     } else {
9976       annz = 0;
9977       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9978       for (q = 0; q < dof; q++) {
9979         a = anchors[rOff + q];
9980         if (a < sStart || a >= sEnd) continue;
9981         PetscCall(PetscSectionGetDof(section, a, &aDof));
9982         annz += aDof;
9983       }
9984       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9985       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9986       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9987     }
9988   }
9989   nnz = i[m];
9990   PetscCall(PetscMalloc1(nnz, &j));
9991   offset = 0;
9992   for (p = pStart; p < pEnd; p++) {
9993     if (numFields) {
9994       for (f = 0; f < numFields; f++) {
9995         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9996         for (q = 0; q < dof; q++) {
9997           PetscInt rDof, rOff, r;
9998           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9999           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10000           for (r = 0; r < rDof; r++) {
10001             PetscInt s;
10002 
10003             a = anchors[rOff + r];
10004             if (a < sStart || a >= sEnd) continue;
10005             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10006             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10007             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10008           }
10009         }
10010       }
10011     } else {
10012       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10013       for (q = 0; q < dof; q++) {
10014         PetscInt rDof, rOff, r;
10015         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10016         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10017         for (r = 0; r < rDof; r++) {
10018           PetscInt s;
10019 
10020           a = anchors[rOff + r];
10021           if (a < sStart || a >= sEnd) continue;
10022           PetscCall(PetscSectionGetDof(section, a, &aDof));
10023           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10024           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10025         }
10026       }
10027     }
10028   }
10029   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10030   PetscCall(PetscFree(i));
10031   PetscCall(PetscFree(j));
10032   PetscCall(ISRestoreIndices(aIS, &anchors));
10033   PetscFunctionReturn(PETSC_SUCCESS);
10034 }
10035 
10036 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10037 {
10038   DM_Plex     *plex = (DM_Plex *)dm->data;
10039   PetscSection anchorSection, section, cSec;
10040   Mat          cMat;
10041 
10042   PetscFunctionBegin;
10043   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10044   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10045   if (anchorSection) {
10046     PetscInt Nf;
10047 
10048     PetscCall(DMGetLocalSection(dm, &section));
10049     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10050     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10051     PetscCall(DMGetNumFields(dm, &Nf));
10052     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10053     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10054     PetscCall(PetscSectionDestroy(&cSec));
10055     PetscCall(MatDestroy(&cMat));
10056   }
10057   PetscFunctionReturn(PETSC_SUCCESS);
10058 }
10059 
10060 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10061 {
10062   IS           subis;
10063   PetscSection section, subsection;
10064 
10065   PetscFunctionBegin;
10066   PetscCall(DMGetLocalSection(dm, &section));
10067   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10068   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10069   /* Create subdomain */
10070   PetscCall(DMPlexFilter(dm, label, value, subdm));
10071   /* Create submodel */
10072   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10073   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10074   PetscCall(DMSetLocalSection(*subdm, subsection));
10075   PetscCall(PetscSectionDestroy(&subsection));
10076   PetscCall(DMCopyDisc(dm, *subdm));
10077   /* Create map from submodel to global model */
10078   if (is) {
10079     PetscSection    sectionGlobal, subsectionGlobal;
10080     IS              spIS;
10081     const PetscInt *spmap;
10082     PetscInt       *subIndices;
10083     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10084     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10085 
10086     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10087     PetscCall(ISGetIndices(spIS, &spmap));
10088     PetscCall(PetscSectionGetNumFields(section, &Nf));
10089     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10090     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10091     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10092     for (p = pStart; p < pEnd; ++p) {
10093       PetscInt gdof, pSubSize = 0;
10094 
10095       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10096       if (gdof > 0) {
10097         for (f = 0; f < Nf; ++f) {
10098           PetscInt fdof, fcdof;
10099 
10100           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10101           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10102           pSubSize += fdof - fcdof;
10103         }
10104         subSize += pSubSize;
10105         if (pSubSize) {
10106           if (bs < 0) {
10107             bs = pSubSize;
10108           } else if (bs != pSubSize) {
10109             /* Layout does not admit a pointwise block size */
10110             bs = 1;
10111           }
10112         }
10113       }
10114     }
10115     /* Must have same blocksize on all procs (some might have no points) */
10116     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10117     bsLocal[1] = bs;
10118     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10119     if (bsMinMax[0] != bsMinMax[1]) {
10120       bs = 1;
10121     } else {
10122       bs = bsMinMax[0];
10123     }
10124     PetscCall(PetscMalloc1(subSize, &subIndices));
10125     for (p = pStart; p < pEnd; ++p) {
10126       PetscInt gdof, goff;
10127 
10128       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10129       if (gdof > 0) {
10130         const PetscInt point = spmap[p];
10131 
10132         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10133         for (f = 0; f < Nf; ++f) {
10134           PetscInt fdof, fcdof, fc, f2, poff = 0;
10135 
10136           /* Can get rid of this loop by storing field information in the global section */
10137           for (f2 = 0; f2 < f; ++f2) {
10138             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10139             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10140             poff += fdof - fcdof;
10141           }
10142           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10143           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10144           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10145         }
10146       }
10147     }
10148     PetscCall(ISRestoreIndices(spIS, &spmap));
10149     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10150     if (bs > 1) {
10151       /* We need to check that the block size does not come from non-contiguous fields */
10152       PetscInt i, j, set = 1;
10153       for (i = 0; i < subSize; i += bs) {
10154         for (j = 0; j < bs; ++j) {
10155           if (subIndices[i + j] != subIndices[i] + j) {
10156             set = 0;
10157             break;
10158           }
10159         }
10160       }
10161       if (set) PetscCall(ISSetBlockSize(*is, bs));
10162     }
10163     /* Attach nullspace */
10164     for (f = 0; f < Nf; ++f) {
10165       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10166       if ((*subdm)->nullspaceConstructors[f]) break;
10167     }
10168     if (f < Nf) {
10169       MatNullSpace nullSpace;
10170       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10171 
10172       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10173       PetscCall(MatNullSpaceDestroy(&nullSpace));
10174     }
10175   }
10176   PetscFunctionReturn(PETSC_SUCCESS);
10177 }
10178 
10179 /*@
10180   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10181 
10182   Input Parameters:
10183 + dm - The `DM`
10184 - dummy - unused argument
10185 
10186   Options Database Key:
10187 . -dm_plex_monitor_throughput - Activate the monitor
10188 
10189   Level: developer
10190 
10191 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10192 @*/
10193 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10194 {
10195 #if defined(PETSC_USE_LOG)
10196   PetscStageLog      stageLog;
10197   PetscLogEvent      event;
10198   PetscLogStage      stage;
10199   PetscEventPerfInfo eventInfo;
10200   PetscReal          cellRate, flopRate;
10201   PetscInt           cStart, cEnd, Nf, N;
10202   const char        *name;
10203 #endif
10204 
10205   PetscFunctionBegin;
10206   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10207 #if defined(PETSC_USE_LOG)
10208   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10209   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10210   PetscCall(DMGetNumFields(dm, &Nf));
10211   PetscCall(PetscLogGetStageLog(&stageLog));
10212   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10213   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10214   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10215   N        = (cEnd - cStart) * Nf * eventInfo.count;
10216   flopRate = eventInfo.flops / eventInfo.time;
10217   cellRate = N / eventInfo.time;
10218   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)));
10219 #else
10220   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10221 #endif
10222   PetscFunctionReturn(PETSC_SUCCESS);
10223 }
10224