xref: /petsc/src/dm/impls/plex/plex.c (revision 0528010d2e36fd843091ae68bca6200af44882d4)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The `DMPLEX` object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(PETSC_SUCCESS);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(PETSC_SUCCESS);
49 }
50 
51 /*@
52   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
53 
54   Input Parameters:
55 + dm     - The `DMPLEX` object
56 - height - The cell height in the Plex, 0 is the default
57 
58   Output Parameters:
59 + cStart - The first "normal" cell
60 - cEnd   - The upper bound on "normal"" cells
61 
62   Level: developer
63 
64   Note:
65   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
66 
67 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(PETSC_SUCCESS);
106 }
107 
108 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
109 {
110   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
111   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
112 
113   PetscFunctionBegin;
114   *ft = PETSC_VTK_INVALID;
115   PetscCall(DMGetCoordinateDim(dm, &cdim));
116   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
118   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
119   if (field >= 0) {
120     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
121     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
122   } else {
123     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
124     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
125   }
126   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(PETSC_SUCCESS);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective
154 
155   Input Parameters:
156 + dm - The `DMPLEX` object
157 . n  - The number of vectors
158 . u  - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
202         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
203         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(PETSC_SUCCESS);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *array;
253   PetscReal          lbound[3], ubound[3];
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
278   PetscCall(PetscDrawClear(draw));
279 
280   /* Could implement something like DMDASelectFields() */
281   for (f = 0; f < Nf; ++f) {
282     DM          fdm = dm;
283     Vec         fv  = v;
284     IS          fis;
285     char        prefix[PETSC_MAX_PATH_LEN];
286     const char *fname;
287 
288     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
289     PetscCall(PetscSectionGetFieldName(s, f, &fname));
290 
291     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
292     else prefix[0] = '\0';
293     if (Nf > 1) {
294       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
295       PetscCall(VecGetSubVector(v, fis, &fv));
296       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
297       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
298     }
299     for (comp = 0; comp < Nc; ++comp, ++w) {
300       PetscInt nmax = 2;
301 
302       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
303       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
304       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
305       PetscCall(PetscDrawSetTitle(draw, title));
306 
307       /* TODO Get max and min only for this component */
308       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
309       if (!flg) {
310         PetscCall(VecMin(fv, NULL, &vbound[0]));
311         PetscCall(VecMax(fv, NULL, &vbound[1]));
312         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
313       }
314 
315       PetscCall(PetscDrawGetPopup(draw, &popup));
316       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
317       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
318       PetscCall(VecGetArrayRead(fv, &array));
319       for (c = cStart; c < cEnd; ++c) {
320         PetscScalar       *coords = NULL, *a = NULL;
321         const PetscScalar *coords_arr;
322         PetscBool          isDG;
323         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
324 
325         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
326         if (a) {
327           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
328           color[1] = color[2] = color[3] = color[0];
329         } else {
330           PetscScalar *vals = NULL;
331           PetscInt     numVals, va;
332 
333           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
334           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
335           switch (numVals / Nc) {
336           case 3: /* P1 Triangle */
337           case 4: /* P1 Quadrangle */
338             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
339             break;
340           case 6: /* P2 Triangle */
341           case 8: /* P2 Quadrangle */
342             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
343             break;
344           default:
345             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
346           }
347           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
348         }
349         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
350         switch (numCoords) {
351         case 6:
352         case 12: /* Localized triangle */
353           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
354           break;
355         case 8:
356         case 16: /* Localized quadrilateral */
357           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
358           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
359           break;
360         default:
361           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
362         }
363         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
364       }
365       PetscCall(VecRestoreArrayRead(fv, &array));
366       PetscCall(PetscDrawFlush(draw));
367       PetscCall(PetscDrawPause(draw));
368       PetscCall(PetscDrawSave(draw));
369     }
370     if (Nf > 1) {
371       PetscCall(VecRestoreSubVector(v, fis, &fv));
372       PetscCall(ISDestroy(&fis));
373       PetscCall(DMDestroy(&fdm));
374     }
375   }
376   PetscFunctionReturn(PETSC_SUCCESS);
377 }
378 
379 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
380 {
381   DM        dm;
382   PetscDraw draw;
383   PetscInt  dim;
384   PetscBool isnull;
385 
386   PetscFunctionBegin;
387   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
388   PetscCall(PetscDrawIsNull(draw, &isnull));
389   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
390 
391   PetscCall(VecGetDM(v, &dm));
392   PetscCall(DMGetCoordinateDim(dm, &dim));
393   switch (dim) {
394   case 1:
395     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
396     break;
397   case 2:
398     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
399     break;
400   default:
401     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
402   }
403   PetscFunctionReturn(PETSC_SUCCESS);
404 }
405 
406 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
407 {
408   DM                      dm;
409   Vec                     locv;
410   const char             *name;
411   PetscSection            section;
412   PetscInt                pStart, pEnd;
413   PetscInt                numFields;
414   PetscViewerVTKFieldType ft;
415 
416   PetscFunctionBegin;
417   PetscCall(VecGetDM(v, &dm));
418   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
419   PetscCall(PetscObjectGetName((PetscObject)v, &name));
420   PetscCall(PetscObjectSetName((PetscObject)locv, name));
421   PetscCall(VecCopy(v, locv));
422   PetscCall(DMGetLocalSection(dm, &section));
423   PetscCall(PetscSectionGetNumFields(section, &numFields));
424   if (!numFields) {
425     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
426     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
427   } else {
428     PetscInt f;
429 
430     for (f = 0; f < numFields; f++) {
431       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
432       if (ft == PETSC_VTK_INVALID) continue;
433       PetscCall(PetscObjectReference((PetscObject)locv));
434       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
435     }
436     PetscCall(VecDestroy(&locv));
437   }
438   PetscFunctionReturn(PETSC_SUCCESS);
439 }
440 
441 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
442 {
443   DM        dm;
444   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
445 
446   PetscFunctionBegin;
447   PetscCall(VecGetDM(v, &dm));
448   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
449   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
450   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
453   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
454   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
455     PetscInt    i, numFields;
456     PetscObject fe;
457     PetscBool   fem  = PETSC_FALSE;
458     Vec         locv = v;
459     const char *name;
460     PetscInt    step;
461     PetscReal   time;
462 
463     PetscCall(DMGetNumFields(dm, &numFields));
464     for (i = 0; i < numFields; i++) {
465       PetscCall(DMGetField(dm, i, NULL, &fe));
466       if (fe->classid == PETSCFE_CLASSID) {
467         fem = PETSC_TRUE;
468         break;
469       }
470     }
471     if (fem) {
472       PetscObject isZero;
473 
474       PetscCall(DMGetLocalVector(dm, &locv));
475       PetscCall(PetscObjectGetName((PetscObject)v, &name));
476       PetscCall(PetscObjectSetName((PetscObject)locv, name));
477       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
478       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
479       PetscCall(VecCopy(v, locv));
480       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
481       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
482     }
483     if (isvtk) {
484       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
485     } else if (ishdf5) {
486 #if defined(PETSC_HAVE_HDF5)
487       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
488 #else
489       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
490 #endif
491     } else if (isdraw) {
492       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
493     } else if (isglvis) {
494       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
495       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
496       PetscCall(VecView_GLVis(locv, viewer));
497     } else if (iscgns) {
498 #if defined(PETSC_HAVE_CGNS)
499       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
500 #else
501       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
502 #endif
503     }
504     if (fem) {
505       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
506       PetscCall(DMRestoreLocalVector(dm, &locv));
507     }
508   } else {
509     PetscBool isseq;
510 
511     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
512     if (isseq) PetscCall(VecView_Seq(v, viewer));
513     else PetscCall(VecView_MPI(v, viewer));
514   }
515   PetscFunctionReturn(PETSC_SUCCESS);
516 }
517 
518 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
519 {
520   DM        dm;
521   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
527   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
528   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
529   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
532   if (isvtk || isdraw || isglvis || iscgns) {
533     Vec         locv;
534     PetscObject isZero;
535     const char *name;
536 
537     PetscCall(DMGetLocalVector(dm, &locv));
538     PetscCall(PetscObjectGetName((PetscObject)v, &name));
539     PetscCall(PetscObjectSetName((PetscObject)locv, name));
540     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
541     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
542     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
543     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
544     PetscCall(VecView_Plex_Local(locv, viewer));
545     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
546     PetscCall(DMRestoreLocalVector(dm, &locv));
547   } else if (ishdf5) {
548 #if defined(PETSC_HAVE_HDF5)
549     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
550 #else
551     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
552 #endif
553   } else if (isexodusii) {
554 #if defined(PETSC_HAVE_EXODUSII)
555     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
556 #else
557     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
558 #endif
559   } else {
560     PetscBool isseq;
561 
562     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
563     if (isseq) PetscCall(VecView_Seq(v, viewer));
564     else PetscCall(VecView_MPI(v, viewer));
565   }
566   PetscFunctionReturn(PETSC_SUCCESS);
567 }
568 
569 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
570 {
571   DM                dm;
572   MPI_Comm          comm;
573   PetscViewerFormat format;
574   Vec               v;
575   PetscBool         isvtk, ishdf5;
576 
577   PetscFunctionBegin;
578   PetscCall(VecGetDM(originalv, &dm));
579   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
580   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
581   PetscCall(PetscViewerGetFormat(viewer, &format));
582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
583   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
584   if (format == PETSC_VIEWER_NATIVE) {
585     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
586     /* this need a better fix */
587     if (dm->useNatural) {
588       if (dm->sfNatural) {
589         const char *vecname;
590         PetscInt    n, nroots;
591 
592         PetscCall(VecGetLocalSize(originalv, &n));
593         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
594         if (n == nroots) {
595           PetscCall(DMPlexCreateNaturalVector(dm, &v));
596           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
597           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
598           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
599           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
600         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
601       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
602     } else v = originalv;
603   } else v = originalv;
604 
605   if (ishdf5) {
606 #if defined(PETSC_HAVE_HDF5)
607     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
608 #else
609     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
610 #endif
611   } else if (isvtk) {
612     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
613   } else {
614     PetscBool isseq;
615 
616     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
617     if (isseq) PetscCall(VecView_Seq(v, viewer));
618     else PetscCall(VecView_MPI(v, viewer));
619   }
620   if (v != originalv) PetscCall(VecDestroy(&v));
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
625 {
626   DM        dm;
627   PetscBool ishdf5;
628 
629   PetscFunctionBegin;
630   PetscCall(VecGetDM(v, &dm));
631   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
633   if (ishdf5) {
634     DM          dmBC;
635     Vec         gv;
636     const char *name;
637 
638     PetscCall(DMGetOutputDM(dm, &dmBC));
639     PetscCall(DMGetGlobalVector(dmBC, &gv));
640     PetscCall(PetscObjectGetName((PetscObject)v, &name));
641     PetscCall(PetscObjectSetName((PetscObject)gv, name));
642     PetscCall(VecLoad_Default(gv, viewer));
643     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
644     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
645     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
646   } else PetscCall(VecLoad_Default(v, viewer));
647   PetscFunctionReturn(PETSC_SUCCESS);
648 }
649 
650 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
651 {
652   DM        dm;
653   PetscBool ishdf5, isexodusii;
654 
655   PetscFunctionBegin;
656   PetscCall(VecGetDM(v, &dm));
657   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
659   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
660   if (ishdf5) {
661 #if defined(PETSC_HAVE_HDF5)
662     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
665 #endif
666   } else if (isexodusii) {
667 #if defined(PETSC_HAVE_EXODUSII)
668     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
669 #else
670     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
671 #endif
672   } else PetscCall(VecLoad_Default(v, viewer));
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   PetscViewerFormat format;
680   PetscBool         ishdf5;
681 
682   PetscFunctionBegin;
683   PetscCall(VecGetDM(originalv, &dm));
684   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
685   PetscCall(PetscViewerGetFormat(viewer, &format));
686   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
687   if (format == PETSC_VIEWER_NATIVE) {
688     if (dm->useNatural) {
689       if (dm->sfNatural) {
690         if (ishdf5) {
691 #if defined(PETSC_HAVE_HDF5)
692           Vec         v;
693           const char *vecname;
694 
695           PetscCall(DMPlexCreateNaturalVector(dm, &v));
696           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
697           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
698           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
699           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
700           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
701           PetscCall(VecDestroy(&v));
702 #else
703           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
704 #endif
705         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
706       }
707     } else PetscCall(VecLoad_Default(originalv, viewer));
708   }
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
713 {
714   PetscSection       coordSection;
715   Vec                coordinates;
716   DMLabel            depthLabel, celltypeLabel;
717   const char        *name[4];
718   const PetscScalar *a;
719   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
720 
721   PetscFunctionBegin;
722   PetscCall(DMGetDimension(dm, &dim));
723   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
724   PetscCall(DMGetCoordinateSection(dm, &coordSection));
725   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
726   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
727   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
728   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
729   PetscCall(VecGetArrayRead(coordinates, &a));
730   name[0]       = "vertex";
731   name[1]       = "edge";
732   name[dim - 1] = "face";
733   name[dim]     = "cell";
734   for (c = cStart; c < cEnd; ++c) {
735     PetscInt *closure = NULL;
736     PetscInt  closureSize, cl, ct;
737 
738     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
739     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
740     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
741     PetscCall(PetscViewerASCIIPushTab(viewer));
742     for (cl = 0; cl < closureSize * 2; cl += 2) {
743       PetscInt point = closure[cl], depth, dof, off, d, p;
744 
745       if ((point < pStart) || (point >= pEnd)) continue;
746       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
747       if (!dof) continue;
748       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
749       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
750       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
751       for (p = 0; p < dof / dim; ++p) {
752         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
753         for (d = 0; d < dim; ++d) {
754           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
755           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
756         }
757         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
758       }
759       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
760     }
761     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
762     PetscCall(PetscViewerASCIIPopTab(viewer));
763   }
764   PetscCall(VecRestoreArrayRead(coordinates, &a));
765   PetscFunctionReturn(PETSC_SUCCESS);
766 }
767 
768 typedef enum {
769   CS_CARTESIAN,
770   CS_POLAR,
771   CS_CYLINDRICAL,
772   CS_SPHERICAL
773 } CoordSystem;
774 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
775 
776 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
777 {
778   PetscInt i;
779 
780   PetscFunctionBegin;
781   if (dim > 3) {
782     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
783   } else {
784     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
785 
786     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
787     switch (cs) {
788     case CS_CARTESIAN:
789       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
790       break;
791     case CS_POLAR:
792       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
793       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
794       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
795       break;
796     case CS_CYLINDRICAL:
797       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       trcoords[2] = coords[2];
801       break;
802     case CS_SPHERICAL:
803       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
804       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
805       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
806       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
807       break;
808     }
809     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
810   }
811   PetscFunctionReturn(PETSC_SUCCESS);
812 }
813 
814 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
815 {
816   DM_Plex          *mesh = (DM_Plex *)dm->data;
817   DM                cdm, cdmCell;
818   PetscSection      coordSection, coordSectionCell;
819   Vec               coordinates, coordinatesCell;
820   PetscViewerFormat format;
821 
822   PetscFunctionBegin;
823   PetscCall(PetscViewerGetFormat(viewer, &format));
824   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
825     const char *name;
826     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
827     PetscInt    pStart, pEnd, p, numLabels, l;
828     PetscMPIInt rank, size;
829 
830     PetscCall(DMGetCoordinateDM(dm, &cdm));
831     PetscCall(DMGetCoordinateSection(dm, &coordSection));
832     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
833     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
834     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
835     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
836     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
837     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
838     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
839     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
840     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
841     PetscCall(DMGetDimension(dm, &dim));
842     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
843     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
844     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
845     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
847     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
848     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
849     for (p = pStart; p < pEnd; ++p) {
850       PetscInt dof, off, s;
851 
852       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
853       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
854       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
855     }
856     PetscCall(PetscViewerFlush(viewer));
857     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
858     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
859     for (p = pStart; p < pEnd; ++p) {
860       PetscInt dof, off, c;
861 
862       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
863       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
864       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
865     }
866     PetscCall(PetscViewerFlush(viewer));
867     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
868     if (coordSection && coordinates) {
869       CoordSystem        cs = CS_CARTESIAN;
870       const PetscScalar *array, *arrayCell = NULL;
871       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
872       PetscMPIInt        rank;
873       const char        *name;
874 
875       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
876       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
877       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
878       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
879       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
880       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
881       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
882       pStart = PetscMin(pvStart, pcStart);
883       pEnd   = PetscMax(pvEnd, pcEnd);
884       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
885       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
886       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
887       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
888 
889       PetscCall(VecGetArrayRead(coordinates, &array));
890       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
891       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
892       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
893       for (p = pStart; p < pEnd; ++p) {
894         PetscInt dof, off;
895 
896         if (p >= pvStart && p < pvEnd) {
897           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
898           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
899           if (dof) {
900             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
901             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
902             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
903           }
904         }
905         if (cdmCell && p >= pcStart && p < pcEnd) {
906           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
907           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
908           if (dof) {
909             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
910             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
911             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
912           }
913         }
914       }
915       PetscCall(PetscViewerFlush(viewer));
916       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
917       PetscCall(VecRestoreArrayRead(coordinates, &array));
918       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
919     }
920     PetscCall(DMGetNumLabels(dm, &numLabels));
921     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
922     for (l = 0; l < numLabels; ++l) {
923       DMLabel     label;
924       PetscBool   isdepth;
925       const char *name;
926 
927       PetscCall(DMGetLabelName(dm, l, &name));
928       PetscCall(PetscStrcmp(name, "depth", &isdepth));
929       if (isdepth) continue;
930       PetscCall(DMGetLabel(dm, name, &label));
931       PetscCall(DMLabelView(label, viewer));
932     }
933     if (size > 1) {
934       PetscSF sf;
935 
936       PetscCall(DMGetPointSF(dm, &sf));
937       PetscCall(PetscSFView(sf, viewer));
938     }
939     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
940     PetscCall(PetscViewerFlush(viewer));
941   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
942     const char  *name, *color;
943     const char  *defcolors[3]  = {"gray", "orange", "green"};
944     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
945     char         lname[PETSC_MAX_PATH_LEN];
946     PetscReal    scale      = 2.0;
947     PetscReal    tikzscale  = 1.0;
948     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
949     double       tcoords[3];
950     PetscScalar *coords;
951     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
952     PetscMPIInt  rank, size;
953     char       **names, **colors, **lcolors;
954     PetscBool    flg, lflg;
955     PetscBT      wp = NULL;
956     PetscInt     pEnd, pStart;
957 
958     PetscCall(DMGetCoordinateDM(dm, &cdm));
959     PetscCall(DMGetCoordinateSection(dm, &coordSection));
960     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
961     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
962     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
963     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
964     PetscCall(DMGetDimension(dm, &dim));
965     PetscCall(DMPlexGetDepth(dm, &depth));
966     PetscCall(DMGetNumLabels(dm, &numLabels));
967     numLabels  = PetscMax(numLabels, 10);
968     numColors  = 10;
969     numLColors = 10;
970     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
971     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
972     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
973     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
974     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
975     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
976     n = 4;
977     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
978     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
979     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
980     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
981     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
982     if (!useLabels) numLabels = 0;
983     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
984     if (!useColors) {
985       numColors = 3;
986       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
987     }
988     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
989     if (!useColors) {
990       numLColors = 4;
991       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
992     }
993     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
994     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
995     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
996     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
997     if (depth < dim) plotEdges = PETSC_FALSE;
998     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
999 
1000     /* filter points with labelvalue != labeldefaultvalue */
1001     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1002     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1003     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1004     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1005     if (lflg) {
1006       DMLabel lbl;
1007 
1008       PetscCall(DMGetLabel(dm, lname, &lbl));
1009       if (lbl) {
1010         PetscInt val, defval;
1011 
1012         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1013         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1014         for (c = pStart; c < pEnd; c++) {
1015           PetscInt *closure = NULL;
1016           PetscInt  closureSize;
1017 
1018           PetscCall(DMLabelGetValue(lbl, c, &val));
1019           if (val == defval) continue;
1020 
1021           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1022           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1023           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1024         }
1025       }
1026     }
1027 
1028     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1029     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1030     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1031     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1032 \\documentclass[tikz]{standalone}\n\n\
1033 \\usepackage{pgflibraryshapes}\n\
1034 \\usetikzlibrary{backgrounds}\n\
1035 \\usetikzlibrary{arrows}\n\
1036 \\begin{document}\n"));
1037     if (size > 1) {
1038       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1039       for (p = 0; p < size; ++p) {
1040         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1041         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1042       }
1043       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1044     }
1045     if (drawHasse) {
1046       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1047 
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1060     }
1061     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1062 
1063     /* Plot vertices */
1064     PetscCall(VecGetArray(coordinates, &coords));
1065     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1066     for (v = vStart; v < vEnd; ++v) {
1067       PetscInt  off, dof, d;
1068       PetscBool isLabeled = PETSC_FALSE;
1069 
1070       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1071       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1072       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1073       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1074       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1075       for (d = 0; d < dof; ++d) {
1076         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1077         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1078       }
1079       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1080       if (dim == 3) {
1081         PetscReal tmp = tcoords[1];
1082         tcoords[1]    = tcoords[2];
1083         tcoords[2]    = -tmp;
1084       }
1085       for (d = 0; d < dof; ++d) {
1086         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1088       }
1089       if (drawHasse) color = colors[0 % numColors];
1090       else color = colors[rank % numColors];
1091       for (l = 0; l < numLabels; ++l) {
1092         PetscInt val;
1093         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1094         if (val >= 0) {
1095           color     = lcolors[l % numLColors];
1096           isLabeled = PETSC_TRUE;
1097           break;
1098         }
1099       }
1100       if (drawNumbers[0]) {
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1102       } else if (drawColors[0]) {
1103         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1104       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1105     }
1106     PetscCall(VecRestoreArray(coordinates, &coords));
1107     PetscCall(PetscViewerFlush(viewer));
1108     /* Plot edges */
1109     if (plotEdges) {
1110       PetscCall(VecGetArray(coordinates, &coords));
1111       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1112       for (e = eStart; e < eEnd; ++e) {
1113         const PetscInt *cone;
1114         PetscInt        coneSize, offA, offB, dof, d;
1115 
1116         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1117         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1118         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1119         PetscCall(DMPlexGetCone(dm, e, &cone));
1120         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1121         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1122         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1123         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1124         for (d = 0; d < dof; ++d) {
1125           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1126           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1127         }
1128         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1129         if (dim == 3) {
1130           PetscReal tmp = tcoords[1];
1131           tcoords[1]    = tcoords[2];
1132           tcoords[2]    = -tmp;
1133         }
1134         for (d = 0; d < dof; ++d) {
1135           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1136           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1137         }
1138         if (drawHasse) color = colors[1 % numColors];
1139         else color = colors[rank % numColors];
1140         for (l = 0; l < numLabels; ++l) {
1141           PetscInt val;
1142           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1143           if (val >= 0) {
1144             color = lcolors[l % numLColors];
1145             break;
1146           }
1147         }
1148         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1149       }
1150       PetscCall(VecRestoreArray(coordinates, &coords));
1151       PetscCall(PetscViewerFlush(viewer));
1152       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1153     }
1154     /* Plot cells */
1155     if (dim == 3 || !drawNumbers[1]) {
1156       for (e = eStart; e < eEnd; ++e) {
1157         const PetscInt *cone;
1158 
1159         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1160         color = colors[rank % numColors];
1161         for (l = 0; l < numLabels; ++l) {
1162           PetscInt val;
1163           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1164           if (val >= 0) {
1165             color = lcolors[l % numLColors];
1166             break;
1167           }
1168         }
1169         PetscCall(DMPlexGetCone(dm, e, &cone));
1170         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1171       }
1172     } else {
1173       DMPolytopeType ct;
1174 
1175       /* Drawing a 2D polygon */
1176       for (c = cStart; c < cEnd; ++c) {
1177         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1178         PetscCall(DMPlexGetCellType(dm, c, &ct));
1179         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1180           const PetscInt *cone;
1181           PetscInt        coneSize, e;
1182 
1183           PetscCall(DMPlexGetCone(dm, c, &cone));
1184           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1185           for (e = 0; e < coneSize; ++e) {
1186             const PetscInt *econe;
1187 
1188             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1189             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1190           }
1191         } else {
1192           PetscInt *closure = NULL;
1193           PetscInt  closureSize, Nv = 0, v;
1194 
1195           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1196           for (p = 0; p < closureSize * 2; p += 2) {
1197             const PetscInt point = closure[p];
1198 
1199             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1200           }
1201           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1202           for (v = 0; v <= Nv; ++v) {
1203             const PetscInt vertex = closure[v % Nv];
1204 
1205             if (v > 0) {
1206               if (plotEdges) {
1207                 const PetscInt *edge;
1208                 PetscInt        endpoints[2], ne;
1209 
1210                 endpoints[0] = closure[v - 1];
1211                 endpoints[1] = vertex;
1212                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1213                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1214                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1215                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1216               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1217             }
1218             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1219           }
1220           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1221           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1222         }
1223       }
1224     }
1225     for (c = cStart; c < cEnd; ++c) {
1226       double             ccoords[3] = {0.0, 0.0, 0.0};
1227       PetscBool          isLabeled  = PETSC_FALSE;
1228       PetscScalar       *cellCoords = NULL;
1229       const PetscScalar *array;
1230       PetscInt           numCoords, cdim, d;
1231       PetscBool          isDG;
1232 
1233       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1234       PetscCall(DMGetCoordinateDim(dm, &cdim));
1235       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1236       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1237       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1238       for (p = 0; p < numCoords / cdim; ++p) {
1239         for (d = 0; d < cdim; ++d) {
1240           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1241           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1242         }
1243         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1244         if (cdim == 3) {
1245           PetscReal tmp = tcoords[1];
1246           tcoords[1]    = tcoords[2];
1247           tcoords[2]    = -tmp;
1248         }
1249         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1250       }
1251       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1252       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1253       for (d = 0; d < cdim; ++d) {
1254         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1255         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1256       }
1257       if (drawHasse) color = colors[depth % numColors];
1258       else color = colors[rank % numColors];
1259       for (l = 0; l < numLabels; ++l) {
1260         PetscInt val;
1261         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1262         if (val >= 0) {
1263           color     = lcolors[l % numLColors];
1264           isLabeled = PETSC_TRUE;
1265           break;
1266         }
1267       }
1268       if (drawNumbers[dim]) {
1269         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1270       } else if (drawColors[dim]) {
1271         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1272       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1273     }
1274     if (drawHasse) {
1275       color = colors[depth % numColors];
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1277       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1279       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1281 
1282       color = colors[1 % numColors];
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1286       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1288 
1289       color = colors[0 % numColors];
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1295 
1296       for (p = pStart; p < pEnd; ++p) {
1297         const PetscInt *cone;
1298         PetscInt        coneSize, cp;
1299 
1300         PetscCall(DMPlexGetCone(dm, p, &cone));
1301         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1302         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1303       }
1304     }
1305     PetscCall(PetscViewerFlush(viewer));
1306     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1307     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1308     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1309     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1310     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1311     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1312     PetscCall(PetscFree3(names, colors, lcolors));
1313     PetscCall(PetscBTDestroy(&wp));
1314   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1315     Vec                    cown, acown;
1316     VecScatter             sct;
1317     ISLocalToGlobalMapping g2l;
1318     IS                     gid, acis;
1319     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1320     MPI_Group              ggroup, ngroup;
1321     PetscScalar           *array, nid;
1322     const PetscInt        *idxs;
1323     PetscInt              *idxs2, *start, *adjacency, *work;
1324     PetscInt64             lm[3], gm[3];
1325     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1326     PetscMPIInt            d1, d2, rank;
1327 
1328     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1329     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1330 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1331     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1332 #endif
1333     if (ncomm != MPI_COMM_NULL) {
1334       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1335       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1336       d1 = 0;
1337       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1338       nid = d2;
1339       PetscCallMPI(MPI_Group_free(&ggroup));
1340       PetscCallMPI(MPI_Group_free(&ngroup));
1341       PetscCallMPI(MPI_Comm_free(&ncomm));
1342     } else nid = 0.0;
1343 
1344     /* Get connectivity */
1345     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1346     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1347 
1348     /* filter overlapped local cells */
1349     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1350     PetscCall(ISGetIndices(gid, &idxs));
1351     PetscCall(ISGetLocalSize(gid, &cum));
1352     PetscCall(PetscMalloc1(cum, &idxs2));
1353     for (c = cStart, cum = 0; c < cEnd; c++) {
1354       if (idxs[c - cStart] < 0) continue;
1355       idxs2[cum++] = idxs[c - cStart];
1356     }
1357     PetscCall(ISRestoreIndices(gid, &idxs));
1358     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1359     PetscCall(ISDestroy(&gid));
1360     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1361 
1362     /* support for node-aware cell locality */
1363     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1364     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1365     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1366     PetscCall(VecGetArray(cown, &array));
1367     for (c = 0; c < numVertices; c++) array[c] = nid;
1368     PetscCall(VecRestoreArray(cown, &array));
1369     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1370     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1371     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1372     PetscCall(ISDestroy(&acis));
1373     PetscCall(VecScatterDestroy(&sct));
1374     PetscCall(VecDestroy(&cown));
1375 
1376     /* compute edgeCut */
1377     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1378     PetscCall(PetscMalloc1(cum, &work));
1379     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1380     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1381     PetscCall(ISDestroy(&gid));
1382     PetscCall(VecGetArray(acown, &array));
1383     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1384       PetscInt totl;
1385 
1386       totl = start[c + 1] - start[c];
1387       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1388       for (i = 0; i < totl; i++) {
1389         if (work[i] < 0) {
1390           ect += 1;
1391           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1392         }
1393       }
1394     }
1395     PetscCall(PetscFree(work));
1396     PetscCall(VecRestoreArray(acown, &array));
1397     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1398     lm[1] = -numVertices;
1399     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1400     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1401     lm[0] = ect;                     /* edgeCut */
1402     lm[1] = ectn;                    /* node-aware edgeCut */
1403     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1404     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1405     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1406 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1407     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1408 #else
1409     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1410 #endif
1411     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1412     PetscCall(PetscFree(start));
1413     PetscCall(PetscFree(adjacency));
1414     PetscCall(VecDestroy(&acown));
1415   } else {
1416     const char    *name;
1417     PetscInt      *sizes, *hybsizes, *ghostsizes;
1418     PetscInt       locDepth, depth, cellHeight, dim, d;
1419     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1420     PetscInt       numLabels, l, maxSize = 17;
1421     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1422     MPI_Comm       comm;
1423     PetscMPIInt    size, rank;
1424 
1425     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1426     PetscCallMPI(MPI_Comm_size(comm, &size));
1427     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1428     PetscCall(DMGetDimension(dm, &dim));
1429     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1430     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1431     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1432     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1433     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1434     PetscCall(DMPlexGetDepth(dm, &locDepth));
1435     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1436     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1437     gcNum = gcEnd - gcStart;
1438     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1439     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1440     for (d = 0; d <= depth; d++) {
1441       PetscInt Nc[2] = {0, 0}, ict;
1442 
1443       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1444       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1445       ict = ct0;
1446       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1447       ct0 = (DMPolytopeType)ict;
1448       for (p = pStart; p < pEnd; ++p) {
1449         DMPolytopeType ct;
1450 
1451         PetscCall(DMPlexGetCellType(dm, p, &ct));
1452         if (ct == ct0) ++Nc[0];
1453         else ++Nc[1];
1454       }
1455       if (size < maxSize) {
1456         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1457         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1458         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1459         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1460         for (p = 0; p < size; ++p) {
1461           if (rank == 0) {
1462             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1463             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1464             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1465           }
1466         }
1467       } else {
1468         PetscInt locMinMax[2];
1469 
1470         locMinMax[0] = Nc[0] + Nc[1];
1471         locMinMax[1] = Nc[0] + Nc[1];
1472         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1473         locMinMax[0] = Nc[1];
1474         locMinMax[1] = Nc[1];
1475         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1476         if (d == depth) {
1477           locMinMax[0] = gcNum;
1478           locMinMax[1] = gcNum;
1479           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1480         }
1481         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1482         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1483         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1484         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1485       }
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1487     }
1488     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1489     {
1490       const PetscReal *maxCell;
1491       const PetscReal *L;
1492       PetscBool        localized;
1493 
1494       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1495       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1496       if (L || localized) {
1497         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1498         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1499         if (L) {
1500           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1501           for (d = 0; d < dim; ++d) {
1502             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1503             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1504           }
1505           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1506         }
1507         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1508         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1509       }
1510     }
1511     PetscCall(DMGetNumLabels(dm, &numLabels));
1512     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1513     for (l = 0; l < numLabels; ++l) {
1514       DMLabel         label;
1515       const char     *name;
1516       IS              valueIS;
1517       const PetscInt *values;
1518       PetscInt        numValues, v;
1519 
1520       PetscCall(DMGetLabelName(dm, l, &name));
1521       PetscCall(DMGetLabel(dm, name, &label));
1522       PetscCall(DMLabelGetNumValues(label, &numValues));
1523       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1524       PetscCall(DMLabelGetValueIS(label, &valueIS));
1525       PetscCall(ISGetIndices(valueIS, &values));
1526       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1527       for (v = 0; v < numValues; ++v) {
1528         PetscInt size;
1529 
1530         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1531         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1532         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1533       }
1534       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1535       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1536       PetscCall(ISRestoreIndices(valueIS, &values));
1537       PetscCall(ISDestroy(&valueIS));
1538     }
1539     {
1540       char    **labelNames;
1541       PetscInt  Nl = numLabels;
1542       PetscBool flg;
1543 
1544       PetscCall(PetscMalloc1(Nl, &labelNames));
1545       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1546       for (l = 0; l < Nl; ++l) {
1547         DMLabel label;
1548 
1549         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1550         if (flg) {
1551           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1552           PetscCall(DMLabelView(label, viewer));
1553         }
1554         PetscCall(PetscFree(labelNames[l]));
1555       }
1556       PetscCall(PetscFree(labelNames));
1557     }
1558     /* If no fields are specified, people do not want to see adjacency */
1559     if (dm->Nf) {
1560       PetscInt f;
1561 
1562       for (f = 0; f < dm->Nf; ++f) {
1563         const char *name;
1564 
1565         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1566         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1567         PetscCall(PetscViewerASCIIPushTab(viewer));
1568         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1569         if (dm->fields[f].adjacency[0]) {
1570           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1571           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1572         } else {
1573           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1574           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1575         }
1576         PetscCall(PetscViewerASCIIPopTab(viewer));
1577       }
1578     }
1579     PetscCall(DMGetCoarseDM(dm, &cdm));
1580     if (cdm) {
1581       PetscCall(PetscViewerASCIIPushTab(viewer));
1582       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1583       PetscCall(DMPlexView_Ascii(cdm, viewer));
1584       PetscCall(PetscViewerASCIIPopTab(viewer));
1585     }
1586   }
1587   PetscFunctionReturn(PETSC_SUCCESS);
1588 }
1589 
1590 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1591 {
1592   DMPolytopeType ct;
1593   PetscMPIInt    rank;
1594   PetscInt       cdim;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   PetscCall(DMGetCoordinateDim(dm, &cdim));
1600   switch (ct) {
1601   case DM_POLYTOPE_SEGMENT:
1602   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1603     switch (cdim) {
1604     case 1: {
1605       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1606       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1607 
1608       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1609       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1610       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1611     } break;
1612     case 2: {
1613       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1614       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1615       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1616 
1617       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1618       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1619       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1620     } break;
1621     default:
1622       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1623     }
1624     break;
1625   case DM_POLYTOPE_TRIANGLE:
1626     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1627     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1628     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1629     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1630     break;
1631   case DM_POLYTOPE_QUADRILATERAL:
1632     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1633     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1635     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1636     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1637     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1638     break;
1639   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1640     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1641     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1642     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1643     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1644     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1645     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1646     break;
1647   case DM_POLYTOPE_FV_GHOST:
1648     break;
1649   default:
1650     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1651   }
1652   PetscFunctionReturn(PETSC_SUCCESS);
1653 }
1654 
1655 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1656 {
1657   DMPolytopeType ct;
1658   PetscReal      centroid[2] = {0., 0.};
1659   PetscMPIInt    rank;
1660   PetscInt       fillColor, v, e, d;
1661 
1662   PetscFunctionBegin;
1663   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1664   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1665   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1666   switch (ct) {
1667   case DM_POLYTOPE_TRIANGLE: {
1668     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1669 
1670     for (v = 0; v < 3; ++v) {
1671       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1672       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1673     }
1674     for (e = 0; e < 3; ++e) {
1675       refCoords[0] = refVertices[e * 2 + 0];
1676       refCoords[1] = refVertices[e * 2 + 1];
1677       for (d = 1; d <= edgeDiv; ++d) {
1678         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1679         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1680       }
1681       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1682       for (d = 0; d < edgeDiv; ++d) {
1683         PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1684         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1685       }
1686     }
1687   } break;
1688   default:
1689     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1690   }
1691   PetscFunctionReturn(PETSC_SUCCESS);
1692 }
1693 
1694 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1695 {
1696   PetscDraw    draw;
1697   DM           cdm;
1698   PetscSection coordSection;
1699   Vec          coordinates;
1700   PetscReal    xyl[3], xyr[3];
1701   PetscReal   *refCoords, *edgeCoords;
1702   PetscBool    isnull, drawAffine = PETSC_TRUE;
1703   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1704 
1705   PetscFunctionBegin;
1706   PetscCall(DMGetCoordinateDim(dm, &dim));
1707   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1708   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1709   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1710   PetscCall(DMGetCoordinateDM(dm, &cdm));
1711   PetscCall(DMGetLocalSection(cdm, &coordSection));
1712   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1713   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1714   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1715 
1716   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1717   PetscCall(PetscDrawIsNull(draw, &isnull));
1718   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1719   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1720 
1721   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1722   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1723   PetscCall(PetscDrawClear(draw));
1724 
1725   for (c = cStart; c < cEnd; ++c) {
1726     PetscScalar       *coords = NULL;
1727     const PetscScalar *coords_arr;
1728     PetscInt           numCoords;
1729     PetscBool          isDG;
1730 
1731     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1732     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1733     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1734     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1735   }
1736   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1737   PetscCall(PetscDrawFlush(draw));
1738   PetscCall(PetscDrawPause(draw));
1739   PetscCall(PetscDrawSave(draw));
1740   PetscFunctionReturn(PETSC_SUCCESS);
1741 }
1742 
1743 #if defined(PETSC_HAVE_EXODUSII)
1744   #include <exodusII.h>
1745   #include <petscviewerexodusii.h>
1746 #endif
1747 
1748 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1749 {
1750   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1751   char      name[PETSC_MAX_PATH_LEN];
1752 
1753   PetscFunctionBegin;
1754   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1755   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1756   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1757   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1759   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1761   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1762   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1763   if (iascii) {
1764     PetscViewerFormat format;
1765     PetscCall(PetscViewerGetFormat(viewer, &format));
1766     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1767     else PetscCall(DMPlexView_Ascii(dm, viewer));
1768   } else if (ishdf5) {
1769 #if defined(PETSC_HAVE_HDF5)
1770     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1771 #else
1772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1773 #endif
1774   } else if (isvtk) {
1775     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1776   } else if (isdraw) {
1777     PetscCall(DMPlexView_Draw(dm, viewer));
1778   } else if (isglvis) {
1779     PetscCall(DMPlexView_GLVis(dm, viewer));
1780 #if defined(PETSC_HAVE_EXODUSII)
1781   } else if (isexodus) {
1782     /*
1783       exodusII requires that all sets be part of exactly one cell set.
1784       If the dm does not have a "Cell Sets" label defined, we create one
1785       with ID 1, containing all cells.
1786       Note that if the Cell Sets label is defined but does not cover all cells,
1787       we may still have a problem. This should probably be checked here or in the viewer;
1788     */
1789     PetscInt numCS;
1790     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1791     if (!numCS) {
1792       PetscInt cStart, cEnd, c;
1793       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1794       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1795       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1796     }
1797     PetscCall(DMView_PlexExodusII(dm, viewer));
1798 #endif
1799 #if defined(PETSC_HAVE_CGNS)
1800   } else if (iscgns) {
1801     PetscCall(DMView_PlexCGNS(dm, viewer));
1802 #endif
1803   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1804   /* Optionally view the partition */
1805   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1806   if (flg) {
1807     Vec ranks;
1808     PetscCall(DMPlexCreateRankField(dm, &ranks));
1809     PetscCall(VecView(ranks, viewer));
1810     PetscCall(VecDestroy(&ranks));
1811   }
1812   /* Optionally view a label */
1813   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1814   if (flg) {
1815     DMLabel label;
1816     Vec     val;
1817 
1818     PetscCall(DMGetLabel(dm, name, &label));
1819     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1820     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1821     PetscCall(VecView(val, viewer));
1822     PetscCall(VecDestroy(&val));
1823   }
1824   PetscFunctionReturn(PETSC_SUCCESS);
1825 }
1826 
1827 /*@
1828   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1829 
1830   Collective
1831 
1832   Input Parameters:
1833 + dm     - The `DM` whose topology is to be saved
1834 - viewer - The `PetscViewer` to save it in
1835 
1836   Level: advanced
1837 
1838 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1839 @*/
1840 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1841 {
1842   PetscBool ishdf5;
1843 
1844   PetscFunctionBegin;
1845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1846   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1847   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1848   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1849   if (ishdf5) {
1850 #if defined(PETSC_HAVE_HDF5)
1851     PetscViewerFormat format;
1852     PetscCall(PetscViewerGetFormat(viewer, &format));
1853     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1854       IS globalPointNumbering;
1855 
1856       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1857       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1858       PetscCall(ISDestroy(&globalPointNumbering));
1859     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1860 #else
1861     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1862 #endif
1863   }
1864   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 /*@
1869   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1870 
1871   Collective
1872 
1873   Input Parameters:
1874 + dm     - The `DM` whose coordinates are to be saved
1875 - viewer - The `PetscViewer` for saving
1876 
1877   Level: advanced
1878 
1879 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1880 @*/
1881 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1882 {
1883   PetscBool ishdf5;
1884 
1885   PetscFunctionBegin;
1886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1887   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1888   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1889   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1890   if (ishdf5) {
1891 #if defined(PETSC_HAVE_HDF5)
1892     PetscViewerFormat format;
1893     PetscCall(PetscViewerGetFormat(viewer, &format));
1894     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1895       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1896     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1897 #else
1898     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1899 #endif
1900   }
1901   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1902   PetscFunctionReturn(PETSC_SUCCESS);
1903 }
1904 
1905 /*@
1906   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1907 
1908   Collective
1909 
1910   Input Parameters:
1911 + dm     - The `DM` whose labels are to be saved
1912 - viewer - The `PetscViewer` for saving
1913 
1914   Level: advanced
1915 
1916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1917 @*/
1918 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1919 {
1920   PetscBool ishdf5;
1921 
1922   PetscFunctionBegin;
1923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1924   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1925   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1926   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1927   if (ishdf5) {
1928 #if defined(PETSC_HAVE_HDF5)
1929     IS                globalPointNumbering;
1930     PetscViewerFormat format;
1931 
1932     PetscCall(PetscViewerGetFormat(viewer, &format));
1933     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1934       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1935       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1936       PetscCall(ISDestroy(&globalPointNumbering));
1937     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1938 #else
1939     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1940 #endif
1941   }
1942   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1943   PetscFunctionReturn(PETSC_SUCCESS);
1944 }
1945 
1946 /*@
1947   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1948 
1949   Collective
1950 
1951   Input Parameters:
1952 + dm         - The `DM` that contains the topology on which the section to be saved is defined
1953 . viewer     - The `PetscViewer` for saving
1954 - sectiondm  - The `DM` that contains the section to be saved
1955 
1956   Level: advanced
1957 
1958   Notes:
1959   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
1960 
1961   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1962 
1963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1964 @*/
1965 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1966 {
1967   PetscBool ishdf5;
1968 
1969   PetscFunctionBegin;
1970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1971   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1972   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1973   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1974   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1975   if (ishdf5) {
1976 #if defined(PETSC_HAVE_HDF5)
1977     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1978 #else
1979     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1980 #endif
1981   }
1982   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1983   PetscFunctionReturn(PETSC_SUCCESS);
1984 }
1985 
1986 /*@
1987   DMPlexGlobalVectorView - Saves a global vector
1988 
1989   Collective
1990 
1991   Input Parameters:
1992 + dm        - The `DM` that represents the topology
1993 . viewer    - The `PetscViewer` to save data with
1994 . sectiondm - The `DM` that contains the global section on which vec is defined
1995 - vec       - The global vector to be saved
1996 
1997   Level: advanced
1998 
1999   Notes:
2000   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2001 
2002   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 inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3609 {
3610   DM_Plex *mesh = (DM_Plex *)dm->data;
3611 
3612   PetscFunctionBeginHot;
3613   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3614     if (useCone) {
3615       PetscCall(DMPlexGetConeSize(dm, p, size));
3616       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3617     } else {
3618       PetscCall(DMPlexGetSupportSize(dm, p, size));
3619       PetscCall(DMPlexGetSupport(dm, p, arr));
3620     }
3621   } else {
3622     if (useCone) {
3623       const PetscSection s   = mesh->coneSection;
3624       const PetscInt     ps  = p - s->pStart;
3625       const PetscInt     off = s->atlasOff[ps];
3626 
3627       *size = s->atlasDof[ps];
3628       *arr  = mesh->cones + off;
3629       *ornt = mesh->coneOrientations + off;
3630     } else {
3631       const PetscSection s   = mesh->supportSection;
3632       const PetscInt     ps  = p - s->pStart;
3633       const PetscInt     off = s->atlasOff[ps];
3634 
3635       *size = s->atlasDof[ps];
3636       *arr  = mesh->supports + off;
3637     }
3638   }
3639   PetscFunctionReturn(PETSC_SUCCESS);
3640 }
3641 
3642 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3643 {
3644   DM_Plex *mesh = (DM_Plex *)dm->data;
3645 
3646   PetscFunctionBeginHot;
3647   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3648     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3649   }
3650   PetscFunctionReturn(PETSC_SUCCESS);
3651 }
3652 
3653 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3654 {
3655   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3656   PetscInt       *closure;
3657   const PetscInt *tmp = NULL, *tmpO = NULL;
3658   PetscInt        off = 0, tmpSize, t;
3659 
3660   PetscFunctionBeginHot;
3661   if (ornt) {
3662     PetscCall(DMPlexGetCellType(dm, p, &ct));
3663     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3664   }
3665   if (*points) {
3666     closure = *points;
3667   } else {
3668     PetscInt maxConeSize, maxSupportSize;
3669     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3670     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3671   }
3672   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3673   if (ct == DM_POLYTOPE_UNKNOWN) {
3674     closure[off++] = p;
3675     closure[off++] = 0;
3676     for (t = 0; t < tmpSize; ++t) {
3677       closure[off++] = tmp[t];
3678       closure[off++] = tmpO ? tmpO[t] : 0;
3679     }
3680   } else {
3681     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3682 
3683     /* We assume that cells with a valid type have faces with a valid type */
3684     closure[off++] = p;
3685     closure[off++] = ornt;
3686     for (t = 0; t < tmpSize; ++t) {
3687       DMPolytopeType ft;
3688 
3689       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3690       closure[off++] = tmp[arr[t]];
3691       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3692     }
3693   }
3694   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3695   if (numPoints) *numPoints = tmpSize + 1;
3696   if (points) *points = closure;
3697   PetscFunctionReturn(PETSC_SUCCESS);
3698 }
3699 
3700 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3701 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3702 {
3703   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3704   const PetscInt *cone, *ornt;
3705   PetscInt       *pts, *closure = NULL;
3706   DMPolytopeType  ft;
3707   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3708   PetscInt        dim, coneSize, c, d, clSize, cl;
3709 
3710   PetscFunctionBeginHot;
3711   PetscCall(DMGetDimension(dm, &dim));
3712   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3713   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3714   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3715   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3716   maxSize       = PetscMax(coneSeries, supportSeries);
3717   if (*points) {
3718     pts = *points;
3719   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3720   c        = 0;
3721   pts[c++] = point;
3722   pts[c++] = o;
3723   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3724   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3725   for (cl = 0; cl < clSize * 2; cl += 2) {
3726     pts[c++] = closure[cl];
3727     pts[c++] = closure[cl + 1];
3728   }
3729   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3730   for (cl = 0; cl < clSize * 2; cl += 2) {
3731     pts[c++] = closure[cl];
3732     pts[c++] = closure[cl + 1];
3733   }
3734   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3735   for (d = 2; d < coneSize; ++d) {
3736     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3737     pts[c++] = cone[arr[d * 2 + 0]];
3738     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3739   }
3740   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3741   if (dim >= 3) {
3742     for (d = 2; d < coneSize; ++d) {
3743       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3744       const PetscInt *fcone, *fornt;
3745       PetscInt        fconeSize, fc, i;
3746 
3747       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3748       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3749       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3750       for (fc = 0; fc < fconeSize; ++fc) {
3751         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3752         const PetscInt co = farr[fc * 2 + 1];
3753 
3754         for (i = 0; i < c; i += 2)
3755           if (pts[i] == cp) break;
3756         if (i == c) {
3757           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3758           pts[c++] = cp;
3759           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3760         }
3761       }
3762       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3763     }
3764   }
3765   *numPoints = c / 2;
3766   *points    = pts;
3767   PetscFunctionReturn(PETSC_SUCCESS);
3768 }
3769 
3770 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3771 {
3772   DMPolytopeType ct;
3773   PetscInt      *closure, *fifo;
3774   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3775   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3776   PetscInt       depth, maxSize;
3777 
3778   PetscFunctionBeginHot;
3779   PetscCall(DMPlexGetDepth(dm, &depth));
3780   if (depth == 1) {
3781     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3782     PetscFunctionReturn(PETSC_SUCCESS);
3783   }
3784   PetscCall(DMPlexGetCellType(dm, p, &ct));
3785   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3786   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3787     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3788     PetscFunctionReturn(PETSC_SUCCESS);
3789   }
3790   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3791   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3792   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3793   maxSize       = PetscMax(coneSeries, supportSeries);
3794   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3795   if (*points) {
3796     closure = *points;
3797   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3798   closure[closureSize++] = p;
3799   closure[closureSize++] = ornt;
3800   fifo[fifoSize++]       = p;
3801   fifo[fifoSize++]       = ornt;
3802   fifo[fifoSize++]       = ct;
3803   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3804   while (fifoSize - fifoStart) {
3805     const PetscInt       q    = fifo[fifoStart++];
3806     const PetscInt       o    = fifo[fifoStart++];
3807     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3808     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3809     const PetscInt      *tmp, *tmpO = NULL;
3810     PetscInt             tmpSize, t;
3811 
3812     if (PetscDefined(USE_DEBUG)) {
3813       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3814       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);
3815     }
3816     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3817     for (t = 0; t < tmpSize; ++t) {
3818       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3819       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3820       const PetscInt cp = tmp[ip];
3821       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3822       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3823       PetscInt       c;
3824 
3825       /* Check for duplicate */
3826       for (c = 0; c < closureSize; c += 2) {
3827         if (closure[c] == cp) break;
3828       }
3829       if (c == closureSize) {
3830         closure[closureSize++] = cp;
3831         closure[closureSize++] = co;
3832         fifo[fifoSize++]       = cp;
3833         fifo[fifoSize++]       = co;
3834         fifo[fifoSize++]       = ct;
3835       }
3836     }
3837     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3838   }
3839   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3840   if (numPoints) *numPoints = closureSize / 2;
3841   if (points) *points = closure;
3842   PetscFunctionReturn(PETSC_SUCCESS);
3843 }
3844 
3845 /*@C
3846   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3847 
3848   Not Collective
3849 
3850   Input Parameters:
3851 + dm      - The `DMPLEX`
3852 . p       - The mesh point
3853 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3854 
3855   Input/Output Parameter:
3856 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3857            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
3858 
3859   Output Parameter:
3860 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3861 
3862   Level: beginner
3863 
3864   Note:
3865   If using internal storage (points is `NULL` on input), each call overwrites the last output.
3866 
3867   Fortran Note:
3868   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
3869 
3870 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3871 @*/
3872 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3873 {
3874   PetscFunctionBeginHot;
3875   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3876   if (numPoints) PetscValidIntPointer(numPoints, 4);
3877   if (points) PetscValidPointer(points, 5);
3878   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3879   PetscFunctionReturn(PETSC_SUCCESS);
3880 }
3881 
3882 /*@C
3883   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3884 
3885   Not Collective
3886 
3887   Input Parameters:
3888 + dm        - The `DMPLEX`
3889 . p         - The mesh point
3890 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3891 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3892 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3893 
3894   Level: beginner
3895 
3896   Note:
3897   If not using internal storage (points is not `NULL` on input), this call is unnecessary
3898 
3899 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3900 @*/
3901 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3902 {
3903   PetscFunctionBeginHot;
3904   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3905   if (numPoints) *numPoints = 0;
3906   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3907   PetscFunctionReturn(PETSC_SUCCESS);
3908 }
3909 
3910 /*@
3911   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3912 
3913   Not Collective
3914 
3915   Input Parameter:
3916 . mesh - The `DMPLEX`
3917 
3918   Output Parameters:
3919 + maxConeSize - The maximum number of in-edges
3920 - maxSupportSize - The maximum number of out-edges
3921 
3922   Level: beginner
3923 
3924 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3925 @*/
3926 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3927 {
3928   DM_Plex *mesh = (DM_Plex *)dm->data;
3929 
3930   PetscFunctionBegin;
3931   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3932   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3933   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3934   PetscFunctionReturn(PETSC_SUCCESS);
3935 }
3936 
3937 PetscErrorCode DMSetUp_Plex(DM dm)
3938 {
3939   DM_Plex *mesh = (DM_Plex *)dm->data;
3940   PetscInt size, maxSupportSize;
3941 
3942   PetscFunctionBegin;
3943   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3944   PetscCall(PetscSectionSetUp(mesh->coneSection));
3945   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3946   PetscCall(PetscMalloc1(size, &mesh->cones));
3947   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3948   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3949   if (maxSupportSize) {
3950     PetscCall(PetscSectionSetUp(mesh->supportSection));
3951     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3952     PetscCall(PetscMalloc1(size, &mesh->supports));
3953   }
3954   PetscFunctionReturn(PETSC_SUCCESS);
3955 }
3956 
3957 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3958 {
3959   PetscFunctionBegin;
3960   if (subdm) PetscCall(DMClone(dm, subdm));
3961   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3962   if (subdm) (*subdm)->useNatural = dm->useNatural;
3963   if (dm->useNatural && dm->sfMigration) {
3964     PetscSF sfNatural;
3965 
3966     (*subdm)->sfMigration = dm->sfMigration;
3967     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3968     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3969     (*subdm)->sfNatural = sfNatural;
3970   }
3971   PetscFunctionReturn(PETSC_SUCCESS);
3972 }
3973 
3974 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3975 {
3976   PetscInt i = 0;
3977 
3978   PetscFunctionBegin;
3979   PetscCall(DMClone(dms[0], superdm));
3980   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3981   (*superdm)->useNatural = PETSC_FALSE;
3982   for (i = 0; i < len; i++) {
3983     if (dms[i]->useNatural && dms[i]->sfMigration) {
3984       PetscSF sfNatural;
3985 
3986       (*superdm)->sfMigration = dms[i]->sfMigration;
3987       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3988       (*superdm)->useNatural = PETSC_TRUE;
3989       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3990       (*superdm)->sfNatural = sfNatural;
3991       break;
3992     }
3993   }
3994   PetscFunctionReturn(PETSC_SUCCESS);
3995 }
3996 
3997 /*@
3998   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3999 
4000   Not Collective
4001 
4002   Input Parameter:
4003 . mesh - The `DMPLEX`
4004 
4005   Level: beginner
4006 
4007   Note:
4008   This should be called after all calls to `DMPlexSetCone()`
4009 
4010 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4011 @*/
4012 PetscErrorCode DMPlexSymmetrize(DM dm)
4013 {
4014   DM_Plex  *mesh = (DM_Plex *)dm->data;
4015   PetscInt *offsets;
4016   PetscInt  supportSize;
4017   PetscInt  pStart, pEnd, p;
4018 
4019   PetscFunctionBegin;
4020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4021   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4022   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4023   /* Calculate support sizes */
4024   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4025   for (p = pStart; p < pEnd; ++p) {
4026     PetscInt dof, off, c;
4027 
4028     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4029     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4030     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4031   }
4032   PetscCall(PetscSectionSetUp(mesh->supportSection));
4033   /* Calculate supports */
4034   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4035   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4036   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4037   for (p = pStart; p < pEnd; ++p) {
4038     PetscInt dof, off, c;
4039 
4040     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4041     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4042     for (c = off; c < off + dof; ++c) {
4043       const PetscInt q = mesh->cones[c];
4044       PetscInt       offS;
4045 
4046       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4047 
4048       mesh->supports[offS + offsets[q]] = p;
4049       ++offsets[q];
4050     }
4051   }
4052   PetscCall(PetscFree(offsets));
4053   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4054   PetscFunctionReturn(PETSC_SUCCESS);
4055 }
4056 
4057 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4058 {
4059   IS stratumIS;
4060 
4061   PetscFunctionBegin;
4062   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4063   if (PetscDefined(USE_DEBUG)) {
4064     PetscInt  qStart, qEnd, numLevels, level;
4065     PetscBool overlap = PETSC_FALSE;
4066     PetscCall(DMLabelGetNumValues(label, &numLevels));
4067     for (level = 0; level < numLevels; level++) {
4068       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4069       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4070         overlap = PETSC_TRUE;
4071         break;
4072       }
4073     }
4074     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);
4075   }
4076   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4077   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4078   PetscCall(ISDestroy(&stratumIS));
4079   PetscFunctionReturn(PETSC_SUCCESS);
4080 }
4081 
4082 /*@
4083   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4084   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4085   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4086   the DAG.
4087 
4088   Collective
4089 
4090   Input Parameter:
4091 . mesh - The `DMPLEX`
4092 
4093   Level: beginner
4094 
4095   Notes:
4096   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4097   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4098   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4099   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4100   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4101 
4102   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4103   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4104   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
4105   to interpolate only that one (e0), so that
4106 .vb
4107   cone(c0) = {e0, v2}
4108   cone(e0) = {v0, v1}
4109 .ve
4110   If `DMPlexStratify()` is run on this mesh, it will give depths
4111 .vb
4112    depth 0 = {v0, v1, v2}
4113    depth 1 = {e0, c0}
4114 .ve
4115   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4116 
4117   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4118 
4119 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4120 @*/
4121 PetscErrorCode DMPlexStratify(DM dm)
4122 {
4123   DM_Plex *mesh = (DM_Plex *)dm->data;
4124   DMLabel  label;
4125   PetscInt pStart, pEnd, p;
4126   PetscInt numRoots = 0, numLeaves = 0;
4127 
4128   PetscFunctionBegin;
4129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4130   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4131 
4132   /* Create depth label */
4133   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4134   PetscCall(DMCreateLabel(dm, "depth"));
4135   PetscCall(DMPlexGetDepthLabel(dm, &label));
4136 
4137   {
4138     /* Initialize roots and count leaves */
4139     PetscInt sMin = PETSC_MAX_INT;
4140     PetscInt sMax = PETSC_MIN_INT;
4141     PetscInt coneSize, supportSize;
4142 
4143     for (p = pStart; p < pEnd; ++p) {
4144       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4145       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4146       if (!coneSize && supportSize) {
4147         sMin = PetscMin(p, sMin);
4148         sMax = PetscMax(p, sMax);
4149         ++numRoots;
4150       } else if (!supportSize && coneSize) {
4151         ++numLeaves;
4152       } else if (!supportSize && !coneSize) {
4153         /* Isolated points */
4154         sMin = PetscMin(p, sMin);
4155         sMax = PetscMax(p, sMax);
4156       }
4157     }
4158     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4159   }
4160 
4161   if (numRoots + numLeaves == (pEnd - pStart)) {
4162     PetscInt sMin = PETSC_MAX_INT;
4163     PetscInt sMax = PETSC_MIN_INT;
4164     PetscInt coneSize, supportSize;
4165 
4166     for (p = pStart; p < pEnd; ++p) {
4167       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4168       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4169       if (!supportSize && coneSize) {
4170         sMin = PetscMin(p, sMin);
4171         sMax = PetscMax(p, sMax);
4172       }
4173     }
4174     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4175   } else {
4176     PetscInt level = 0;
4177     PetscInt qStart, qEnd, q;
4178 
4179     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4180     while (qEnd > qStart) {
4181       PetscInt sMin = PETSC_MAX_INT;
4182       PetscInt sMax = PETSC_MIN_INT;
4183 
4184       for (q = qStart; q < qEnd; ++q) {
4185         const PetscInt *support;
4186         PetscInt        supportSize, s;
4187 
4188         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4189         PetscCall(DMPlexGetSupport(dm, q, &support));
4190         for (s = 0; s < supportSize; ++s) {
4191           sMin = PetscMin(support[s], sMin);
4192           sMax = PetscMax(support[s], sMax);
4193         }
4194       }
4195       PetscCall(DMLabelGetNumValues(label, &level));
4196       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4197       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4198     }
4199   }
4200   { /* just in case there is an empty process */
4201     PetscInt numValues, maxValues = 0, v;
4202 
4203     PetscCall(DMLabelGetNumValues(label, &numValues));
4204     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4205     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4206   }
4207   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4208   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4209   PetscFunctionReturn(PETSC_SUCCESS);
4210 }
4211 
4212 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4213 {
4214   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4215   PetscInt       dim, depth, pheight, coneSize;
4216 
4217   PetscFunctionBeginHot;
4218   PetscCall(DMGetDimension(dm, &dim));
4219   PetscCall(DMPlexGetDepth(dm, &depth));
4220   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4221   pheight = depth - pdepth;
4222   if (depth <= 1) {
4223     switch (pdepth) {
4224     case 0:
4225       ct = DM_POLYTOPE_POINT;
4226       break;
4227     case 1:
4228       switch (coneSize) {
4229       case 2:
4230         ct = DM_POLYTOPE_SEGMENT;
4231         break;
4232       case 3:
4233         ct = DM_POLYTOPE_TRIANGLE;
4234         break;
4235       case 4:
4236         switch (dim) {
4237         case 2:
4238           ct = DM_POLYTOPE_QUADRILATERAL;
4239           break;
4240         case 3:
4241           ct = DM_POLYTOPE_TETRAHEDRON;
4242           break;
4243         default:
4244           break;
4245         }
4246         break;
4247       case 5:
4248         ct = DM_POLYTOPE_PYRAMID;
4249         break;
4250       case 6:
4251         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4252         break;
4253       case 8:
4254         ct = DM_POLYTOPE_HEXAHEDRON;
4255         break;
4256       default:
4257         break;
4258       }
4259     }
4260   } else {
4261     if (pdepth == 0) {
4262       ct = DM_POLYTOPE_POINT;
4263     } else if (pheight == 0) {
4264       switch (dim) {
4265       case 1:
4266         switch (coneSize) {
4267         case 2:
4268           ct = DM_POLYTOPE_SEGMENT;
4269           break;
4270         default:
4271           break;
4272         }
4273         break;
4274       case 2:
4275         switch (coneSize) {
4276         case 3:
4277           ct = DM_POLYTOPE_TRIANGLE;
4278           break;
4279         case 4:
4280           ct = DM_POLYTOPE_QUADRILATERAL;
4281           break;
4282         default:
4283           break;
4284         }
4285         break;
4286       case 3:
4287         switch (coneSize) {
4288         case 4:
4289           ct = DM_POLYTOPE_TETRAHEDRON;
4290           break;
4291         case 5: {
4292           const PetscInt *cone;
4293           PetscInt        faceConeSize;
4294 
4295           PetscCall(DMPlexGetCone(dm, p, &cone));
4296           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4297           switch (faceConeSize) {
4298           case 3:
4299             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4300             break;
4301           case 4:
4302             ct = DM_POLYTOPE_PYRAMID;
4303             break;
4304           }
4305         } break;
4306         case 6:
4307           ct = DM_POLYTOPE_HEXAHEDRON;
4308           break;
4309         default:
4310           break;
4311         }
4312         break;
4313       default:
4314         break;
4315       }
4316     } else if (pheight > 0) {
4317       switch (coneSize) {
4318       case 2:
4319         ct = DM_POLYTOPE_SEGMENT;
4320         break;
4321       case 3:
4322         ct = DM_POLYTOPE_TRIANGLE;
4323         break;
4324       case 4:
4325         ct = DM_POLYTOPE_QUADRILATERAL;
4326         break;
4327       default:
4328         break;
4329       }
4330     }
4331   }
4332   *pt = ct;
4333   PetscFunctionReturn(PETSC_SUCCESS);
4334 }
4335 
4336 /*@
4337   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4338 
4339   Collective
4340 
4341   Input Parameter:
4342 . mesh - The `DMPLEX`
4343 
4344   Level: developer
4345 
4346   Note:
4347   This function is normally called automatically when a cell type is requested. It creates an
4348   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4349   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4350 
4351   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4352 
4353 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4354 @*/
4355 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4356 {
4357   DM_Plex *mesh;
4358   DMLabel  ctLabel;
4359   PetscInt pStart, pEnd, p;
4360 
4361   PetscFunctionBegin;
4362   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4363   mesh = (DM_Plex *)dm->data;
4364   PetscCall(DMCreateLabel(dm, "celltype"));
4365   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4366   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4367   PetscCall(PetscFree(mesh->cellTypes));
4368   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4369   for (p = pStart; p < pEnd; ++p) {
4370     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4371     PetscInt       pdepth;
4372 
4373     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4374     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4375     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4376     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4377     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4378   }
4379   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4380   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4381   PetscFunctionReturn(PETSC_SUCCESS);
4382 }
4383 
4384 /*@C
4385   DMPlexGetJoin - Get an array for the join of the set of points
4386 
4387   Not Collective
4388 
4389   Input Parameters:
4390 + dm - The `DMPLEX` object
4391 . numPoints - The number of input points for the join
4392 - points - The input points
4393 
4394   Output Parameters:
4395 + numCoveredPoints - The number of points in the join
4396 - coveredPoints - The points in the join
4397 
4398   Level: intermediate
4399 
4400   Note:
4401   Currently, this is restricted to a single level join
4402 
4403   Fortran Note:
4404   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4405 
4406 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4407 @*/
4408 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4409 {
4410   DM_Plex  *mesh = (DM_Plex *)dm->data;
4411   PetscInt *join[2];
4412   PetscInt  joinSize, i = 0;
4413   PetscInt  dof, off, p, c, m;
4414   PetscInt  maxSupportSize;
4415 
4416   PetscFunctionBegin;
4417   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4418   PetscValidIntPointer(points, 3);
4419   PetscValidIntPointer(numCoveredPoints, 4);
4420   PetscValidPointer(coveredPoints, 5);
4421   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4422   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4423   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4424   /* Copy in support of first point */
4425   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4426   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4427   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4428   /* Check each successive support */
4429   for (p = 1; p < numPoints; ++p) {
4430     PetscInt newJoinSize = 0;
4431 
4432     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4433     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4434     for (c = 0; c < dof; ++c) {
4435       const PetscInt point = mesh->supports[off + c];
4436 
4437       for (m = 0; m < joinSize; ++m) {
4438         if (point == join[i][m]) {
4439           join[1 - i][newJoinSize++] = point;
4440           break;
4441         }
4442       }
4443     }
4444     joinSize = newJoinSize;
4445     i        = 1 - i;
4446   }
4447   *numCoveredPoints = joinSize;
4448   *coveredPoints    = join[i];
4449   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4450   PetscFunctionReturn(PETSC_SUCCESS);
4451 }
4452 
4453 /*@C
4454   DMPlexRestoreJoin - Restore an array for the join of the set of points
4455 
4456   Not Collective
4457 
4458   Input Parameters:
4459 + dm - The `DMPLEX` object
4460 . numPoints - The number of input points for the join
4461 - points - The input points
4462 
4463   Output Parameters:
4464 + numCoveredPoints - The number of points in the join
4465 - coveredPoints - The points in the join
4466 
4467   Level: intermediate
4468 
4469   Fortran Note:
4470   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4471 
4472 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4473 @*/
4474 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4475 {
4476   PetscFunctionBegin;
4477   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4478   if (points) PetscValidIntPointer(points, 3);
4479   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4480   PetscValidPointer(coveredPoints, 5);
4481   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4482   if (numCoveredPoints) *numCoveredPoints = 0;
4483   PetscFunctionReturn(PETSC_SUCCESS);
4484 }
4485 
4486 /*@C
4487   DMPlexGetFullJoin - Get an array for the join of the set of points
4488 
4489   Not Collective
4490 
4491   Input Parameters:
4492 + dm - The `DMPLEX` object
4493 . numPoints - The number of input points for the join
4494 - points - The input points
4495 
4496   Output Parameters:
4497 + numCoveredPoints - The number of points in the join
4498 - coveredPoints - The points in the join
4499 
4500   Level: intermediate
4501 
4502   Fortran Note:
4503   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4504 
4505 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4506 @*/
4507 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4508 {
4509   PetscInt *offsets, **closures;
4510   PetscInt *join[2];
4511   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4512   PetscInt  p, d, c, m, ms;
4513 
4514   PetscFunctionBegin;
4515   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4516   PetscValidIntPointer(points, 3);
4517   PetscValidIntPointer(numCoveredPoints, 4);
4518   PetscValidPointer(coveredPoints, 5);
4519 
4520   PetscCall(DMPlexGetDepth(dm, &depth));
4521   PetscCall(PetscCalloc1(numPoints, &closures));
4522   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4523   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4524   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4525   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4526   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4527 
4528   for (p = 0; p < numPoints; ++p) {
4529     PetscInt closureSize;
4530 
4531     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4532 
4533     offsets[p * (depth + 2) + 0] = 0;
4534     for (d = 0; d < depth + 1; ++d) {
4535       PetscInt pStart, pEnd, i;
4536 
4537       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4538       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4539         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4540           offsets[p * (depth + 2) + d + 1] = i;
4541           break;
4542         }
4543       }
4544       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4545     }
4546     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);
4547   }
4548   for (d = 0; d < depth + 1; ++d) {
4549     PetscInt dof;
4550 
4551     /* Copy in support of first point */
4552     dof = offsets[d + 1] - offsets[d];
4553     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4554     /* Check each successive cone */
4555     for (p = 1; p < numPoints && joinSize; ++p) {
4556       PetscInt newJoinSize = 0;
4557 
4558       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4559       for (c = 0; c < dof; ++c) {
4560         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4561 
4562         for (m = 0; m < joinSize; ++m) {
4563           if (point == join[i][m]) {
4564             join[1 - i][newJoinSize++] = point;
4565             break;
4566           }
4567         }
4568       }
4569       joinSize = newJoinSize;
4570       i        = 1 - i;
4571     }
4572     if (joinSize) break;
4573   }
4574   *numCoveredPoints = joinSize;
4575   *coveredPoints    = join[i];
4576   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4577   PetscCall(PetscFree(closures));
4578   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4579   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4580   PetscFunctionReturn(PETSC_SUCCESS);
4581 }
4582 
4583 /*@C
4584   DMPlexGetMeet - Get an array for the meet of the set of points
4585 
4586   Not Collective
4587 
4588   Input Parameters:
4589 + dm - The `DMPLEX` object
4590 . numPoints - The number of input points for the meet
4591 - points - The input points
4592 
4593   Output Parameters:
4594 + numCoveredPoints - The number of points in the meet
4595 - coveredPoints - The points in the meet
4596 
4597   Level: intermediate
4598 
4599   Note:
4600   Currently, this is restricted to a single level meet
4601 
4602   Fortran Notes:
4603   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4604 
4605 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4606 @*/
4607 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4608 {
4609   DM_Plex  *mesh = (DM_Plex *)dm->data;
4610   PetscInt *meet[2];
4611   PetscInt  meetSize, i = 0;
4612   PetscInt  dof, off, p, c, m;
4613   PetscInt  maxConeSize;
4614 
4615   PetscFunctionBegin;
4616   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4617   PetscValidIntPointer(points, 3);
4618   PetscValidIntPointer(numCoveringPoints, 4);
4619   PetscValidPointer(coveringPoints, 5);
4620   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4621   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4622   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4623   /* Copy in cone of first point */
4624   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4625   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4626   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4627   /* Check each successive cone */
4628   for (p = 1; p < numPoints; ++p) {
4629     PetscInt newMeetSize = 0;
4630 
4631     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4632     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4633     for (c = 0; c < dof; ++c) {
4634       const PetscInt point = mesh->cones[off + c];
4635 
4636       for (m = 0; m < meetSize; ++m) {
4637         if (point == meet[i][m]) {
4638           meet[1 - i][newMeetSize++] = point;
4639           break;
4640         }
4641       }
4642     }
4643     meetSize = newMeetSize;
4644     i        = 1 - i;
4645   }
4646   *numCoveringPoints = meetSize;
4647   *coveringPoints    = meet[i];
4648   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4649   PetscFunctionReturn(PETSC_SUCCESS);
4650 }
4651 
4652 /*@C
4653   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4654 
4655   Not Collective
4656 
4657   Input Parameters:
4658 + dm - The `DMPLEX` object
4659 . numPoints - The number of input points for the meet
4660 - points - The input points
4661 
4662   Output Parameters:
4663 + numCoveredPoints - The number of points in the meet
4664 - coveredPoints - The points in the meet
4665 
4666   Level: intermediate
4667 
4668   Fortran Note:
4669   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4670 
4671 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4672 @*/
4673 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4674 {
4675   PetscFunctionBegin;
4676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4677   if (points) PetscValidIntPointer(points, 3);
4678   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4679   PetscValidPointer(coveredPoints, 5);
4680   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4681   if (numCoveredPoints) *numCoveredPoints = 0;
4682   PetscFunctionReturn(PETSC_SUCCESS);
4683 }
4684 
4685 /*@C
4686   DMPlexGetFullMeet - Get an array for the meet of the set of points
4687 
4688   Not Collective
4689 
4690   Input Parameters:
4691 + dm - The `DMPLEX` object
4692 . numPoints - The number of input points for the meet
4693 - points - The input points
4694 
4695   Output Parameters:
4696 + numCoveredPoints - The number of points in the meet
4697 - coveredPoints - The points in the meet
4698 
4699   Level: intermediate
4700 
4701   Fortran Note:
4702   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4703 
4704 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4705 @*/
4706 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4707 {
4708   PetscInt *offsets, **closures;
4709   PetscInt *meet[2];
4710   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4711   PetscInt  p, h, c, m, mc;
4712 
4713   PetscFunctionBegin;
4714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4715   PetscValidIntPointer(points, 3);
4716   PetscValidIntPointer(numCoveredPoints, 4);
4717   PetscValidPointer(coveredPoints, 5);
4718 
4719   PetscCall(DMPlexGetDepth(dm, &height));
4720   PetscCall(PetscMalloc1(numPoints, &closures));
4721   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4722   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4723   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4724   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4725   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4726 
4727   for (p = 0; p < numPoints; ++p) {
4728     PetscInt closureSize;
4729 
4730     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4731 
4732     offsets[p * (height + 2) + 0] = 0;
4733     for (h = 0; h < height + 1; ++h) {
4734       PetscInt pStart, pEnd, i;
4735 
4736       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4737       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4738         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4739           offsets[p * (height + 2) + h + 1] = i;
4740           break;
4741         }
4742       }
4743       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4744     }
4745     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);
4746   }
4747   for (h = 0; h < height + 1; ++h) {
4748     PetscInt dof;
4749 
4750     /* Copy in cone of first point */
4751     dof = offsets[h + 1] - offsets[h];
4752     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4753     /* Check each successive cone */
4754     for (p = 1; p < numPoints && meetSize; ++p) {
4755       PetscInt newMeetSize = 0;
4756 
4757       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4758       for (c = 0; c < dof; ++c) {
4759         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4760 
4761         for (m = 0; m < meetSize; ++m) {
4762           if (point == meet[i][m]) {
4763             meet[1 - i][newMeetSize++] = point;
4764             break;
4765           }
4766         }
4767       }
4768       meetSize = newMeetSize;
4769       i        = 1 - i;
4770     }
4771     if (meetSize) break;
4772   }
4773   *numCoveredPoints = meetSize;
4774   *coveredPoints    = meet[i];
4775   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4776   PetscCall(PetscFree(closures));
4777   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4778   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4779   PetscFunctionReturn(PETSC_SUCCESS);
4780 }
4781 
4782 /*@C
4783   DMPlexEqual - Determine if two `DM` have the same topology
4784 
4785   Not Collective
4786 
4787   Input Parameters:
4788 + dmA - A `DMPLEX` object
4789 - dmB - A `DMPLEX` object
4790 
4791   Output Parameter:
4792 . equal - `PETSC_TRUE` if the topologies are identical
4793 
4794   Level: intermediate
4795 
4796   Note:
4797   We are not solving graph isomorphism, so we do not permute.
4798 
4799 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4800 @*/
4801 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4802 {
4803   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4804 
4805   PetscFunctionBegin;
4806   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4807   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4808   PetscValidBoolPointer(equal, 3);
4809 
4810   *equal = PETSC_FALSE;
4811   PetscCall(DMPlexGetDepth(dmA, &depth));
4812   PetscCall(DMPlexGetDepth(dmB, &depthB));
4813   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4814   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4815   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4816   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4817   for (p = pStart; p < pEnd; ++p) {
4818     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4819     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4820 
4821     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4822     PetscCall(DMPlexGetCone(dmA, p, &cone));
4823     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4824     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4825     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4826     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4827     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4828     for (c = 0; c < coneSize; ++c) {
4829       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4830       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4831     }
4832     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4833     PetscCall(DMPlexGetSupport(dmA, p, &support));
4834     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4835     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4836     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4837     for (s = 0; s < supportSize; ++s) {
4838       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4839     }
4840   }
4841   *equal = PETSC_TRUE;
4842   PetscFunctionReturn(PETSC_SUCCESS);
4843 }
4844 
4845 /*@C
4846   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4847 
4848   Not Collective
4849 
4850   Input Parameters:
4851 + dm         - The `DMPLEX`
4852 . cellDim    - The cell dimension
4853 - numCorners - The number of vertices on a cell
4854 
4855   Output Parameter:
4856 . numFaceVertices - The number of vertices on a face
4857 
4858   Level: developer
4859 
4860   Note:
4861   Of course this can only work for a restricted set of symmetric shapes
4862 
4863 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4864 @*/
4865 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4866 {
4867   MPI_Comm comm;
4868 
4869   PetscFunctionBegin;
4870   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4871   PetscValidIntPointer(numFaceVertices, 4);
4872   switch (cellDim) {
4873   case 0:
4874     *numFaceVertices = 0;
4875     break;
4876   case 1:
4877     *numFaceVertices = 1;
4878     break;
4879   case 2:
4880     switch (numCorners) {
4881     case 3:                 /* triangle */
4882       *numFaceVertices = 2; /* Edge has 2 vertices */
4883       break;
4884     case 4:                 /* quadrilateral */
4885       *numFaceVertices = 2; /* Edge has 2 vertices */
4886       break;
4887     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4888       *numFaceVertices = 3; /* Edge has 3 vertices */
4889       break;
4890     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4891       *numFaceVertices = 3; /* Edge has 3 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   case 3:
4898     switch (numCorners) {
4899     case 4:                 /* tetradehdron */
4900       *numFaceVertices = 3; /* Face has 3 vertices */
4901       break;
4902     case 6:                 /* tet cohesive cells */
4903       *numFaceVertices = 4; /* Face has 4 vertices */
4904       break;
4905     case 8:                 /* hexahedron */
4906       *numFaceVertices = 4; /* Face has 4 vertices */
4907       break;
4908     case 9:                 /* tet cohesive Lagrange cells */
4909       *numFaceVertices = 6; /* Face has 6 vertices */
4910       break;
4911     case 10:                /* quadratic tetrahedron */
4912       *numFaceVertices = 6; /* Face has 6 vertices */
4913       break;
4914     case 12:                /* hex cohesive Lagrange cells */
4915       *numFaceVertices = 6; /* Face has 6 vertices */
4916       break;
4917     case 18:                /* quadratic tet cohesive Lagrange cells */
4918       *numFaceVertices = 6; /* Face has 6 vertices */
4919       break;
4920     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4921       *numFaceVertices = 9; /* Face has 9 vertices */
4922       break;
4923     default:
4924       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4925     }
4926     break;
4927   default:
4928     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4929   }
4930   PetscFunctionReturn(PETSC_SUCCESS);
4931 }
4932 
4933 /*@
4934   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4935 
4936   Not Collective
4937 
4938   Input Parameter:
4939 . dm    - The `DMPLEX` object
4940 
4941   Output Parameter:
4942 . depthLabel - The `DMLabel` recording point depth
4943 
4944   Level: developer
4945 
4946 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4947 @*/
4948 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4949 {
4950   PetscFunctionBegin;
4951   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4952   PetscValidPointer(depthLabel, 2);
4953   *depthLabel = dm->depthLabel;
4954   PetscFunctionReturn(PETSC_SUCCESS);
4955 }
4956 
4957 /*@
4958   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4959 
4960   Not Collective
4961 
4962   Input Parameter:
4963 . dm    - The `DMPLEX` object
4964 
4965   Output Parameter:
4966 . depth - The number of strata (breadth first levels) in the DAG
4967 
4968   Level: developer
4969 
4970   Notes:
4971   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4972 
4973   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4974 
4975   An empty mesh gives -1.
4976 
4977 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4978 @*/
4979 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4980 {
4981   DM_Plex *mesh = (DM_Plex *)dm->data;
4982   DMLabel  label;
4983   PetscInt d = 0;
4984 
4985   PetscFunctionBegin;
4986   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4987   PetscValidIntPointer(depth, 2);
4988   if (mesh->tr) {
4989     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4990   } else {
4991     PetscCall(DMPlexGetDepthLabel(dm, &label));
4992     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4993     *depth = d - 1;
4994   }
4995   PetscFunctionReturn(PETSC_SUCCESS);
4996 }
4997 
4998 /*@
4999   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5000 
5001   Not Collective
5002 
5003   Input Parameters:
5004 + dm    - The `DMPLEX` object
5005 - depth - The requested depth
5006 
5007   Output Parameters:
5008 + start - The first point at this `depth`
5009 - end   - One beyond the last point at this `depth`
5010 
5011   Level: developer
5012 
5013   Notes:
5014   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5015   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5016   higher dimension, e.g., "edges".
5017 
5018 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5019 @*/
5020 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5021 {
5022   DM_Plex *mesh = (DM_Plex *)dm->data;
5023   DMLabel  label;
5024   PetscInt pStart, pEnd;
5025 
5026   PetscFunctionBegin;
5027   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5028   if (start) {
5029     PetscValidIntPointer(start, 3);
5030     *start = 0;
5031   }
5032   if (end) {
5033     PetscValidIntPointer(end, 4);
5034     *end = 0;
5035   }
5036   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5037   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5038   if (depth < 0) {
5039     if (start) *start = pStart;
5040     if (end) *end = pEnd;
5041     PetscFunctionReturn(PETSC_SUCCESS);
5042   }
5043   if (mesh->tr) {
5044     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5045   } else {
5046     PetscCall(DMPlexGetDepthLabel(dm, &label));
5047     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5048     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5049   }
5050   PetscFunctionReturn(PETSC_SUCCESS);
5051 }
5052 
5053 /*@
5054   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5055 
5056   Not Collective
5057 
5058   Input Parameters:
5059 + dm     - The `DMPLEX` object
5060 - height - The requested height
5061 
5062   Output Parameters:
5063 + start - The first point at this `height`
5064 - end   - One beyond the last point at this `height`
5065 
5066   Level: developer
5067 
5068   Notes:
5069   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5070   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5071   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5072 
5073 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5074 @*/
5075 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5076 {
5077   DMLabel  label;
5078   PetscInt depth, pStart, pEnd;
5079 
5080   PetscFunctionBegin;
5081   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5082   if (start) {
5083     PetscValidIntPointer(start, 3);
5084     *start = 0;
5085   }
5086   if (end) {
5087     PetscValidIntPointer(end, 4);
5088     *end = 0;
5089   }
5090   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5091   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5092   if (height < 0) {
5093     if (start) *start = pStart;
5094     if (end) *end = pEnd;
5095     PetscFunctionReturn(PETSC_SUCCESS);
5096   }
5097   PetscCall(DMPlexGetDepthLabel(dm, &label));
5098   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5099   PetscCall(DMLabelGetNumValues(label, &depth));
5100   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5101   PetscFunctionReturn(PETSC_SUCCESS);
5102 }
5103 
5104 /*@
5105   DMPlexGetPointDepth - Get the `depth` of a given point
5106 
5107   Not Collective
5108 
5109   Input Parameters:
5110 + dm    - The `DMPLEX` object
5111 - point - The point
5112 
5113   Output Parameter:
5114 . depth - The depth of the `point`
5115 
5116   Level: intermediate
5117 
5118 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5119 @*/
5120 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5121 {
5122   PetscFunctionBegin;
5123   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5124   PetscValidIntPointer(depth, 3);
5125   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5126   PetscFunctionReturn(PETSC_SUCCESS);
5127 }
5128 
5129 /*@
5130   DMPlexGetPointHeight - Get the `height` of a given point
5131 
5132   Not Collective
5133 
5134   Input Parameters:
5135 + dm    - The `DMPLEX` object
5136 - point - The point
5137 
5138   Output Parameter:
5139 . height - The height of the `point`
5140 
5141   Level: intermediate
5142 
5143 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5144 @*/
5145 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5146 {
5147   PetscInt n, pDepth;
5148 
5149   PetscFunctionBegin;
5150   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5151   PetscValidIntPointer(height, 3);
5152   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5153   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5154   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5155   PetscFunctionReturn(PETSC_SUCCESS);
5156 }
5157 
5158 /*@
5159   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5160 
5161   Not Collective
5162 
5163   Input Parameter:
5164 . dm - The `DMPLEX` object
5165 
5166   Output Parameter:
5167 . celltypeLabel - The `DMLabel` recording cell polytope type
5168 
5169   Level: developer
5170 
5171   Note:
5172   This function will trigger automatica computation of cell types. This can be disabled by calling
5173   `DMCreateLabel`(dm, "celltype") beforehand.
5174 
5175 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5176 @*/
5177 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5178 {
5179   PetscFunctionBegin;
5180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5181   PetscValidPointer(celltypeLabel, 2);
5182   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5183   *celltypeLabel = dm->celltypeLabel;
5184   PetscFunctionReturn(PETSC_SUCCESS);
5185 }
5186 
5187 /*@
5188   DMPlexGetCellType - Get the polytope type of a given cell
5189 
5190   Not Collective
5191 
5192   Input Parameters:
5193 + dm   - The `DMPLEX` object
5194 - cell - The cell
5195 
5196   Output Parameter:
5197 . celltype - The polytope type of the cell
5198 
5199   Level: intermediate
5200 
5201 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5202 @*/
5203 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5204 {
5205   DM_Plex *mesh = (DM_Plex *)dm->data;
5206   DMLabel  label;
5207   PetscInt ct;
5208 
5209   PetscFunctionBegin;
5210   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5211   PetscValidPointer(celltype, 3);
5212   if (mesh->tr) {
5213     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5214   } else {
5215     PetscInt pStart, pEnd;
5216 
5217     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5218     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5219       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5220       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5221       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5222       for (PetscInt p = pStart; p < pEnd; p++) {
5223         PetscCall(DMLabelGetValue(label, p, &ct));
5224         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5225       }
5226     }
5227     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5228     if (PetscDefined(USE_DEBUG)) {
5229       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5230       PetscCall(DMLabelGetValue(label, cell, &ct));
5231       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5232       PetscCheck(ct == *celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5233     }
5234   }
5235   PetscFunctionReturn(PETSC_SUCCESS);
5236 }
5237 
5238 /*@
5239   DMPlexSetCellType - Set the polytope type of a given cell
5240 
5241   Not Collective
5242 
5243   Input Parameters:
5244 + dm   - The `DMPLEX` object
5245 . cell - The cell
5246 - celltype - The polytope type of the cell
5247 
5248   Level: advanced
5249 
5250   Note:
5251   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5252   is executed. This function will override the computed type. However, if automatic classification will not succeed
5253   and a user wants to manually specify all types, the classification must be disabled by calling
5254   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5255 
5256 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5257 @*/
5258 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5259 {
5260   DM_Plex *mesh = (DM_Plex *)dm->data;
5261   DMLabel  label;
5262   PetscInt pStart, pEnd;
5263 
5264   PetscFunctionBegin;
5265   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5266   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5267   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5268   PetscCall(DMLabelSetValue(label, cell, celltype));
5269   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5270   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5271   PetscFunctionReturn(PETSC_SUCCESS);
5272 }
5273 
5274 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5275 {
5276   PetscSection section, s;
5277   Mat          m;
5278   PetscInt     maxHeight;
5279   const char  *prefix;
5280 
5281   PetscFunctionBegin;
5282   PetscCall(DMClone(dm, cdm));
5283   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5284   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5285   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5286   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5287   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5288   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5289   PetscCall(DMSetLocalSection(*cdm, section));
5290   PetscCall(PetscSectionDestroy(&section));
5291   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5292   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5293   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5294   PetscCall(PetscSectionDestroy(&s));
5295   PetscCall(MatDestroy(&m));
5296 
5297   PetscCall(DMSetNumFields(*cdm, 1));
5298   PetscCall(DMCreateDS(*cdm));
5299   (*cdm)->cloneOpts = PETSC_TRUE;
5300   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5301   PetscFunctionReturn(PETSC_SUCCESS);
5302 }
5303 
5304 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5305 {
5306   Vec coordsLocal, cellCoordsLocal;
5307   DM  coordsDM, cellCoordsDM;
5308 
5309   PetscFunctionBegin;
5310   *field = NULL;
5311   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5312   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5313   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5314   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5315   if (coordsLocal && coordsDM) {
5316     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5317     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5318   }
5319   PetscFunctionReturn(PETSC_SUCCESS);
5320 }
5321 
5322 /*@C
5323   DMPlexGetConeSection - Return a section which describes the layout of cone data
5324 
5325   Not Collective
5326 
5327   Input Parameter:
5328 . dm        - The `DMPLEX` object
5329 
5330   Output Parameter:
5331 . section - The `PetscSection` object
5332 
5333   Level: developer
5334 
5335 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5336 @*/
5337 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5338 {
5339   DM_Plex *mesh = (DM_Plex *)dm->data;
5340 
5341   PetscFunctionBegin;
5342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5343   if (section) *section = mesh->coneSection;
5344   PetscFunctionReturn(PETSC_SUCCESS);
5345 }
5346 
5347 /*@C
5348   DMPlexGetSupportSection - Return a section which describes the layout of support data
5349 
5350   Not Collective
5351 
5352   Input Parameter:
5353 . dm        - The `DMPLEX` object
5354 
5355   Output Parameter:
5356 . section - The `PetscSection` object
5357 
5358   Level: developer
5359 
5360 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5361 @*/
5362 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5363 {
5364   DM_Plex *mesh = (DM_Plex *)dm->data;
5365 
5366   PetscFunctionBegin;
5367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5368   if (section) *section = mesh->supportSection;
5369   PetscFunctionReturn(PETSC_SUCCESS);
5370 }
5371 
5372 /*@C
5373   DMPlexGetCones - Return cone data
5374 
5375   Not Collective
5376 
5377   Input Parameter:
5378 . dm        - The `DMPLEX` object
5379 
5380   Output Parameter:
5381 . cones - The cone for each point
5382 
5383   Level: developer
5384 
5385 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5386 @*/
5387 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5388 {
5389   DM_Plex *mesh = (DM_Plex *)dm->data;
5390 
5391   PetscFunctionBegin;
5392   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5393   if (cones) *cones = mesh->cones;
5394   PetscFunctionReturn(PETSC_SUCCESS);
5395 }
5396 
5397 /*@C
5398   DMPlexGetConeOrientations - Return cone orientation data
5399 
5400   Not Collective
5401 
5402   Input Parameter:
5403 . dm        - The `DMPLEX` object
5404 
5405   Output Parameter:
5406 . coneOrientations - The array of cone orientations for all points
5407 
5408   Level: developer
5409 
5410   Notes:
5411   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5412 
5413   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5414 
5415 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5416 @*/
5417 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5418 {
5419   DM_Plex *mesh = (DM_Plex *)dm->data;
5420 
5421   PetscFunctionBegin;
5422   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5423   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5424   PetscFunctionReturn(PETSC_SUCCESS);
5425 }
5426 
5427 /******************************** FEM Support **********************************/
5428 
5429 /*
5430  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5431  representing a line in the section.
5432 */
5433 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5434 {
5435   PetscFunctionBeginHot;
5436   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5437   if (line < 0) {
5438     *k  = 0;
5439     *Nc = 0;
5440   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5441     *k = 1;
5442   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5443     /* An order k SEM disc has k-1 dofs on an edge */
5444     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5445     *k = *k / *Nc + 1;
5446   }
5447   PetscFunctionReturn(PETSC_SUCCESS);
5448 }
5449 
5450 /*@
5451 
5452   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5453   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5454   section provided (or the section of the `DM`).
5455 
5456   Input Parameters:
5457 + dm      - The `DM`
5458 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5459 - section - The `PetscSection` to reorder, or `NULL` for the default section
5460 
5461   Example:
5462   A typical interpolated single-quad mesh might order points as
5463 .vb
5464   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5465 
5466   v4 -- e6 -- v3
5467   |           |
5468   e7    c0    e8
5469   |           |
5470   v1 -- e5 -- v2
5471 .ve
5472 
5473   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5474   dofs in the order of points, e.g.,
5475 .vb
5476     c0 -> [0,1,2,3]
5477     v1 -> [4]
5478     ...
5479     e5 -> [8, 9]
5480 .ve
5481 
5482   which corresponds to the dofs
5483 .vb
5484     6   10  11  7
5485     13  2   3   15
5486     12  0   1   14
5487     4   8   9   5
5488 .ve
5489 
5490   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5491 .vb
5492   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5493 .ve
5494 
5495   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5496 .vb
5497    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5498 .ve
5499 
5500   Level: developer
5501 
5502   Note:
5503   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5504   degree of the basis.
5505 
5506 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5507 @*/
5508 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5509 {
5510   DMLabel   label;
5511   PetscInt  dim, depth = -1, eStart = -1, Nf;
5512   PetscBool vertexchart;
5513 
5514   PetscFunctionBegin;
5515   PetscCall(DMGetDimension(dm, &dim));
5516   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5517   if (point < 0) {
5518     PetscInt sStart, sEnd;
5519 
5520     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5521     point = sEnd - sStart ? sStart : point;
5522   }
5523   PetscCall(DMPlexGetDepthLabel(dm, &label));
5524   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5525   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5526   if (depth == 1) {
5527     eStart = point;
5528   } else if (depth == dim) {
5529     const PetscInt *cone;
5530 
5531     PetscCall(DMPlexGetCone(dm, point, &cone));
5532     if (dim == 2) eStart = cone[0];
5533     else if (dim == 3) {
5534       const PetscInt *cone2;
5535       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5536       eStart = cone2[0];
5537     } 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);
5538   } 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);
5539   { /* Determine whether the chart covers all points or just vertices. */
5540     PetscInt pStart, pEnd, cStart, cEnd;
5541     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5542     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5543     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5544     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5545     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5546   }
5547   PetscCall(PetscSectionGetNumFields(section, &Nf));
5548   for (PetscInt d = 1; d <= dim; d++) {
5549     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5550     PetscInt *perm;
5551 
5552     for (f = 0; f < Nf; ++f) {
5553       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5554       size += PetscPowInt(k + 1, d) * Nc;
5555     }
5556     PetscCall(PetscMalloc1(size, &perm));
5557     for (f = 0; f < Nf; ++f) {
5558       switch (d) {
5559       case 1:
5560         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5561         /*
5562          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5563          We want              [ vtx0; edge of length k-1; vtx1 ]
5564          */
5565         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5566         for (i = 0; i < k - 1; i++)
5567           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5568         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5569         foffset = offset;
5570         break;
5571       case 2:
5572         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5573         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5574         /* The SEM order is
5575 
5576          v_lb, {e_b}, v_rb,
5577          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5578          v_lt, reverse {e_t}, v_rt
5579          */
5580         {
5581           const PetscInt of   = 0;
5582           const PetscInt oeb  = of + PetscSqr(k - 1);
5583           const PetscInt oer  = oeb + (k - 1);
5584           const PetscInt oet  = oer + (k - 1);
5585           const PetscInt oel  = oet + (k - 1);
5586           const PetscInt ovlb = oel + (k - 1);
5587           const PetscInt ovrb = ovlb + 1;
5588           const PetscInt ovrt = ovrb + 1;
5589           const PetscInt ovlt = ovrt + 1;
5590           PetscInt       o;
5591 
5592           /* bottom */
5593           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5594           for (o = oeb; o < oer; ++o)
5595             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5596           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5597           /* middle */
5598           for (i = 0; i < k - 1; ++i) {
5599             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5600             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5601               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5602             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5603           }
5604           /* top */
5605           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5606           for (o = oel - 1; o >= oet; --o)
5607             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5608           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5609           foffset = offset;
5610         }
5611         break;
5612       case 3:
5613         /* The original hex closure is
5614 
5615          {c,
5616          f_b, f_t, f_f, f_b, f_r, f_l,
5617          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5618          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5619          */
5620         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5621         /* The SEM order is
5622          Bottom Slice
5623          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5624          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5625          v_blb, {e_bb}, v_brb,
5626 
5627          Middle Slice (j)
5628          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5629          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5630          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5631 
5632          Top Slice
5633          v_tlf, {e_tf}, v_trf,
5634          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5635          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5636          */
5637         {
5638           const PetscInt oc    = 0;
5639           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5640           const PetscInt oft   = ofb + PetscSqr(k - 1);
5641           const PetscInt off   = oft + PetscSqr(k - 1);
5642           const PetscInt ofk   = off + PetscSqr(k - 1);
5643           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5644           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5645           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5646           const PetscInt oebb  = oebl + (k - 1);
5647           const PetscInt oebr  = oebb + (k - 1);
5648           const PetscInt oebf  = oebr + (k - 1);
5649           const PetscInt oetf  = oebf + (k - 1);
5650           const PetscInt oetr  = oetf + (k - 1);
5651           const PetscInt oetb  = oetr + (k - 1);
5652           const PetscInt oetl  = oetb + (k - 1);
5653           const PetscInt oerf  = oetl + (k - 1);
5654           const PetscInt oelf  = oerf + (k - 1);
5655           const PetscInt oelb  = oelf + (k - 1);
5656           const PetscInt oerb  = oelb + (k - 1);
5657           const PetscInt ovblf = oerb + (k - 1);
5658           const PetscInt ovblb = ovblf + 1;
5659           const PetscInt ovbrb = ovblb + 1;
5660           const PetscInt ovbrf = ovbrb + 1;
5661           const PetscInt ovtlf = ovbrf + 1;
5662           const PetscInt ovtrf = ovtlf + 1;
5663           const PetscInt ovtrb = ovtrf + 1;
5664           const PetscInt ovtlb = ovtrb + 1;
5665           PetscInt       o, n;
5666 
5667           /* Bottom Slice */
5668           /*   bottom */
5669           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5670           for (o = oetf - 1; o >= oebf; --o)
5671             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5672           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5673           /*   middle */
5674           for (i = 0; i < k - 1; ++i) {
5675             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5676             for (n = 0; n < k - 1; ++n) {
5677               o = ofb + n * (k - 1) + i;
5678               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5679             }
5680             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5681           }
5682           /*   top */
5683           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5684           for (o = oebb; o < oebr; ++o)
5685             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5686           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5687 
5688           /* Middle Slice */
5689           for (j = 0; j < k - 1; ++j) {
5690             /*   bottom */
5691             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5692             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5693               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5694             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5695             /*   middle */
5696             for (i = 0; i < k - 1; ++i) {
5697               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5698               for (n = 0; n < k - 1; ++n)
5699                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5700               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5701             }
5702             /*   top */
5703             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5704             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5705               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5706             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5707           }
5708 
5709           /* Top Slice */
5710           /*   bottom */
5711           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5712           for (o = oetf; o < oetr; ++o)
5713             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5714           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5715           /*   middle */
5716           for (i = 0; i < k - 1; ++i) {
5717             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5718             for (n = 0; n < k - 1; ++n)
5719               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5720             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5721           }
5722           /*   top */
5723           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5724           for (o = oetl - 1; o >= oetb; --o)
5725             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5726           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5727 
5728           foffset = offset;
5729         }
5730         break;
5731       default:
5732         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5733       }
5734     }
5735     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5736     /* Check permutation */
5737     {
5738       PetscInt *check;
5739 
5740       PetscCall(PetscMalloc1(size, &check));
5741       for (i = 0; i < size; ++i) {
5742         check[i] = -1;
5743         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5744       }
5745       for (i = 0; i < size; ++i) check[perm[i]] = i;
5746       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5747       PetscCall(PetscFree(check));
5748     }
5749     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5750     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5751       PetscInt *loc_perm;
5752       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5753       for (PetscInt i = 0; i < size; i++) {
5754         loc_perm[i]        = perm[i];
5755         loc_perm[size + i] = size + perm[i];
5756       }
5757       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5758     }
5759   }
5760   PetscFunctionReturn(PETSC_SUCCESS);
5761 }
5762 
5763 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5764 {
5765   PetscDS  prob;
5766   PetscInt depth, Nf, h;
5767   DMLabel  label;
5768 
5769   PetscFunctionBeginHot;
5770   PetscCall(DMGetDS(dm, &prob));
5771   Nf      = prob->Nf;
5772   label   = dm->depthLabel;
5773   *dspace = NULL;
5774   if (field < Nf) {
5775     PetscObject disc = prob->disc[field];
5776 
5777     if (disc->classid == PETSCFE_CLASSID) {
5778       PetscDualSpace dsp;
5779 
5780       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5781       PetscCall(DMLabelGetNumValues(label, &depth));
5782       PetscCall(DMLabelGetValue(label, point, &h));
5783       h = depth - 1 - h;
5784       if (h) {
5785         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5786       } else {
5787         *dspace = dsp;
5788       }
5789     }
5790   }
5791   PetscFunctionReturn(PETSC_SUCCESS);
5792 }
5793 
5794 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5795 {
5796   PetscScalar       *array;
5797   const PetscScalar *vArray;
5798   const PetscInt    *cone, *coneO;
5799   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5800 
5801   PetscFunctionBeginHot;
5802   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5803   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5804   PetscCall(DMPlexGetCone(dm, point, &cone));
5805   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5806   if (!values || !*values) {
5807     if ((point >= pStart) && (point < pEnd)) {
5808       PetscInt dof;
5809 
5810       PetscCall(PetscSectionGetDof(section, point, &dof));
5811       size += dof;
5812     }
5813     for (p = 0; p < numPoints; ++p) {
5814       const PetscInt cp = cone[p];
5815       PetscInt       dof;
5816 
5817       if ((cp < pStart) || (cp >= pEnd)) continue;
5818       PetscCall(PetscSectionGetDof(section, cp, &dof));
5819       size += dof;
5820     }
5821     if (!values) {
5822       if (csize) *csize = size;
5823       PetscFunctionReturn(PETSC_SUCCESS);
5824     }
5825     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5826   } else {
5827     array = *values;
5828   }
5829   size = 0;
5830   PetscCall(VecGetArrayRead(v, &vArray));
5831   if ((point >= pStart) && (point < pEnd)) {
5832     PetscInt           dof, off, d;
5833     const PetscScalar *varr;
5834 
5835     PetscCall(PetscSectionGetDof(section, point, &dof));
5836     PetscCall(PetscSectionGetOffset(section, point, &off));
5837     varr = &vArray[off];
5838     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5839     size += dof;
5840   }
5841   for (p = 0; p < numPoints; ++p) {
5842     const PetscInt     cp = cone[p];
5843     PetscInt           o  = coneO[p];
5844     PetscInt           dof, off, d;
5845     const PetscScalar *varr;
5846 
5847     if ((cp < pStart) || (cp >= pEnd)) continue;
5848     PetscCall(PetscSectionGetDof(section, cp, &dof));
5849     PetscCall(PetscSectionGetOffset(section, cp, &off));
5850     varr = &vArray[off];
5851     if (o >= 0) {
5852       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5853     } else {
5854       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5855     }
5856     size += dof;
5857   }
5858   PetscCall(VecRestoreArrayRead(v, &vArray));
5859   if (!*values) {
5860     if (csize) *csize = size;
5861     *values = array;
5862   } else {
5863     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5864     *csize = size;
5865   }
5866   PetscFunctionReturn(PETSC_SUCCESS);
5867 }
5868 
5869 /* Compress out points not in the section */
5870 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5871 {
5872   const PetscInt np = *numPoints;
5873   PetscInt       pStart, pEnd, p, q;
5874 
5875   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5876   for (p = 0, q = 0; p < np; ++p) {
5877     const PetscInt r = points[p * 2];
5878     if ((r >= pStart) && (r < pEnd)) {
5879       points[q * 2]     = r;
5880       points[q * 2 + 1] = points[p * 2 + 1];
5881       ++q;
5882     }
5883   }
5884   *numPoints = q;
5885   return PETSC_SUCCESS;
5886 }
5887 
5888 /* Compressed closure does not apply closure permutation */
5889 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5890 {
5891   const PetscInt *cla = NULL;
5892   PetscInt        np, *pts = NULL;
5893 
5894   PetscFunctionBeginHot;
5895   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5896   if (!ornt && *clPoints) {
5897     PetscInt dof, off;
5898 
5899     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5900     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5901     PetscCall(ISGetIndices(*clPoints, &cla));
5902     np  = dof / 2;
5903     pts = (PetscInt *)&cla[off];
5904   } else {
5905     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
5906     PetscCall(CompressPoints_Private(section, &np, pts));
5907   }
5908   *numPoints = np;
5909   *points    = pts;
5910   *clp       = cla;
5911   PetscFunctionReturn(PETSC_SUCCESS);
5912 }
5913 
5914 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5915 {
5916   PetscFunctionBeginHot;
5917   if (!*clPoints) {
5918     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5919   } else {
5920     PetscCall(ISRestoreIndices(*clPoints, clp));
5921   }
5922   *numPoints = 0;
5923   *points    = NULL;
5924   *clSec     = NULL;
5925   *clPoints  = NULL;
5926   *clp       = NULL;
5927   PetscFunctionReturn(PETSC_SUCCESS);
5928 }
5929 
5930 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5931 {
5932   PetscInt            offset = 0, p;
5933   const PetscInt    **perms  = NULL;
5934   const PetscScalar **flips  = NULL;
5935 
5936   PetscFunctionBeginHot;
5937   *size = 0;
5938   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5939   for (p = 0; p < numPoints; p++) {
5940     const PetscInt     point = points[2 * p];
5941     const PetscInt    *perm  = perms ? perms[p] : NULL;
5942     const PetscScalar *flip  = flips ? flips[p] : NULL;
5943     PetscInt           dof, off, d;
5944     const PetscScalar *varr;
5945 
5946     PetscCall(PetscSectionGetDof(section, point, &dof));
5947     PetscCall(PetscSectionGetOffset(section, point, &off));
5948     varr = &vArray[off];
5949     if (clperm) {
5950       if (perm) {
5951         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5952       } else {
5953         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5954       }
5955       if (flip) {
5956         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5957       }
5958     } else {
5959       if (perm) {
5960         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5961       } else {
5962         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5963       }
5964       if (flip) {
5965         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5966       }
5967     }
5968     offset += dof;
5969   }
5970   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5971   *size = offset;
5972   PetscFunctionReturn(PETSC_SUCCESS);
5973 }
5974 
5975 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[])
5976 {
5977   PetscInt offset = 0, f;
5978 
5979   PetscFunctionBeginHot;
5980   *size = 0;
5981   for (f = 0; f < numFields; ++f) {
5982     PetscInt            p;
5983     const PetscInt    **perms = NULL;
5984     const PetscScalar **flips = NULL;
5985 
5986     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5987     for (p = 0; p < numPoints; p++) {
5988       const PetscInt     point = points[2 * p];
5989       PetscInt           fdof, foff, b;
5990       const PetscScalar *varr;
5991       const PetscInt    *perm = perms ? perms[p] : NULL;
5992       const PetscScalar *flip = flips ? flips[p] : NULL;
5993 
5994       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5995       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5996       varr = &vArray[foff];
5997       if (clperm) {
5998         if (perm) {
5999           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6000         } else {
6001           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6002         }
6003         if (flip) {
6004           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6005         }
6006       } else {
6007         if (perm) {
6008           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6009         } else {
6010           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6011         }
6012         if (flip) {
6013           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6014         }
6015       }
6016       offset += fdof;
6017     }
6018     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6019   }
6020   *size = offset;
6021   PetscFunctionReturn(PETSC_SUCCESS);
6022 }
6023 
6024 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6025 {
6026   PetscSection    clSection;
6027   IS              clPoints;
6028   PetscInt       *points = NULL;
6029   const PetscInt *clp, *perm;
6030   PetscInt        depth, numFields, numPoints, asize;
6031 
6032   PetscFunctionBeginHot;
6033   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6034   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6035   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6036   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6037   PetscCall(DMPlexGetDepth(dm, &depth));
6038   PetscCall(PetscSectionGetNumFields(section, &numFields));
6039   if (depth == 1 && numFields < 2) {
6040     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6041     PetscFunctionReturn(PETSC_SUCCESS);
6042   }
6043   /* Get points */
6044   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6045   /* Get sizes */
6046   asize = 0;
6047   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6048     PetscInt dof;
6049     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6050     asize += dof;
6051   }
6052   if (values) {
6053     const PetscScalar *vArray;
6054     PetscInt           size;
6055 
6056     if (*values) {
6057       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);
6058     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6059     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6060     PetscCall(VecGetArrayRead(v, &vArray));
6061     /* Get values */
6062     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6063     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6064     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6065     /* Cleanup array */
6066     PetscCall(VecRestoreArrayRead(v, &vArray));
6067   }
6068   if (csize) *csize = asize;
6069   /* Cleanup points */
6070   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6071   PetscFunctionReturn(PETSC_SUCCESS);
6072 }
6073 
6074 /*@C
6075   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6076 
6077   Not collective
6078 
6079   Input Parameters:
6080 + dm - The `DM`
6081 . section - The section describing the layout in `v`, or `NULL` to use the default section
6082 . v - The local vector
6083 - point - The point in the `DM`
6084 
6085   Input/Output Parameters:
6086 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6087 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6088            if the user provided `NULL`, it is a borrowed array and should not be freed
6089 
6090   Level: intermediate
6091 
6092   Notes:
6093   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6094   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6095   assembly function, and a user may already have allocated storage for this operation.
6096 
6097   A typical use could be
6098 .vb
6099    values = NULL;
6100    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6101    for (cl = 0; cl < clSize; ++cl) {
6102      <Compute on closure>
6103    }
6104    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6105 .ve
6106   or
6107 .vb
6108    PetscMalloc1(clMaxSize, &values);
6109    for (p = pStart; p < pEnd; ++p) {
6110      clSize = clMaxSize;
6111      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6112      for (cl = 0; cl < clSize; ++cl) {
6113        <Compute on closure>
6114      }
6115    }
6116    PetscFree(values);
6117 .ve
6118 
6119   Fortran Note:
6120   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6121 
6122 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6123 @*/
6124 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6125 {
6126   PetscFunctionBeginHot;
6127   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, v, point, 0, csize, values));
6128   PetscFunctionReturn(PETSC_SUCCESS);
6129 }
6130 
6131 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6132 {
6133   DMLabel            depthLabel;
6134   PetscSection       clSection;
6135   IS                 clPoints;
6136   PetscScalar       *array;
6137   const PetscScalar *vArray;
6138   PetscInt          *points = NULL;
6139   const PetscInt    *clp, *perm = NULL;
6140   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6141 
6142   PetscFunctionBeginHot;
6143   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6144   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6145   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6146   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6147   PetscCall(DMPlexGetDepth(dm, &mdepth));
6148   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6149   PetscCall(PetscSectionGetNumFields(section, &numFields));
6150   if (mdepth == 1 && numFields < 2) {
6151     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6152     PetscFunctionReturn(PETSC_SUCCESS);
6153   }
6154   /* Get points */
6155   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6156   for (clsize = 0, p = 0; p < Np; p++) {
6157     PetscInt dof;
6158     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6159     clsize += dof;
6160   }
6161   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6162   /* Filter points */
6163   for (p = 0; p < numPoints * 2; p += 2) {
6164     PetscInt dep;
6165 
6166     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6167     if (dep != depth) continue;
6168     points[Np * 2 + 0] = points[p];
6169     points[Np * 2 + 1] = points[p + 1];
6170     ++Np;
6171   }
6172   /* Get array */
6173   if (!values || !*values) {
6174     PetscInt asize = 0, dof;
6175 
6176     for (p = 0; p < Np * 2; p += 2) {
6177       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6178       asize += dof;
6179     }
6180     if (!values) {
6181       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6182       if (csize) *csize = asize;
6183       PetscFunctionReturn(PETSC_SUCCESS);
6184     }
6185     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6186   } else {
6187     array = *values;
6188   }
6189   PetscCall(VecGetArrayRead(v, &vArray));
6190   /* Get values */
6191   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6192   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6193   /* Cleanup points */
6194   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6195   /* Cleanup array */
6196   PetscCall(VecRestoreArrayRead(v, &vArray));
6197   if (!*values) {
6198     if (csize) *csize = size;
6199     *values = array;
6200   } else {
6201     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6202     *csize = size;
6203   }
6204   PetscFunctionReturn(PETSC_SUCCESS);
6205 }
6206 
6207 /*@C
6208   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6209 
6210   Not collective
6211 
6212   Input Parameters:
6213 + dm - The `DM`
6214 . section - The section describing the layout in `v`, or `NULL` to use the default section
6215 . v - The local vector
6216 . point - The point in the `DM`
6217 . csize - The number of values in the closure, or `NULL`
6218 - values - The array of values, which is a borrowed array and should not be freed
6219 
6220   Level: intermediate
6221 
6222   Note:
6223   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6224 
6225   Fortran Note:
6226   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6227 
6228 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6229 @*/
6230 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6231 {
6232   PetscInt size = 0;
6233 
6234   PetscFunctionBegin;
6235   /* Should work without recalculating size */
6236   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6237   *values = NULL;
6238   PetscFunctionReturn(PETSC_SUCCESS);
6239 }
6240 
6241 static inline void add(PetscScalar *x, PetscScalar y)
6242 {
6243   *x += y;
6244 }
6245 static inline void insert(PetscScalar *x, PetscScalar y)
6246 {
6247   *x = y;
6248 }
6249 
6250 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[])
6251 {
6252   PetscInt        cdof;  /* The number of constraints on this point */
6253   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6254   PetscScalar    *a;
6255   PetscInt        off, cind = 0, k;
6256 
6257   PetscFunctionBegin;
6258   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6259   PetscCall(PetscSectionGetOffset(section, point, &off));
6260   a = &array[off];
6261   if (!cdof || setBC) {
6262     if (clperm) {
6263       if (perm) {
6264         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6265       } else {
6266         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6267       }
6268     } else {
6269       if (perm) {
6270         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6271       } else {
6272         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6273       }
6274     }
6275   } else {
6276     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6277     if (clperm) {
6278       if (perm) {
6279         for (k = 0; k < dof; ++k) {
6280           if ((cind < cdof) && (k == cdofs[cind])) {
6281             ++cind;
6282             continue;
6283           }
6284           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6285         }
6286       } else {
6287         for (k = 0; k < dof; ++k) {
6288           if ((cind < cdof) && (k == cdofs[cind])) {
6289             ++cind;
6290             continue;
6291           }
6292           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6293         }
6294       }
6295     } else {
6296       if (perm) {
6297         for (k = 0; k < dof; ++k) {
6298           if ((cind < cdof) && (k == cdofs[cind])) {
6299             ++cind;
6300             continue;
6301           }
6302           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6303         }
6304       } else {
6305         for (k = 0; k < dof; ++k) {
6306           if ((cind < cdof) && (k == cdofs[cind])) {
6307             ++cind;
6308             continue;
6309           }
6310           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6311         }
6312       }
6313     }
6314   }
6315   PetscFunctionReturn(PETSC_SUCCESS);
6316 }
6317 
6318 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[])
6319 {
6320   PetscInt        cdof;  /* The number of constraints on this point */
6321   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6322   PetscScalar    *a;
6323   PetscInt        off, cind = 0, k;
6324 
6325   PetscFunctionBegin;
6326   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6327   PetscCall(PetscSectionGetOffset(section, point, &off));
6328   a = &array[off];
6329   if (cdof) {
6330     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6331     if (clperm) {
6332       if (perm) {
6333         for (k = 0; k < dof; ++k) {
6334           if ((cind < cdof) && (k == cdofs[cind])) {
6335             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6336             cind++;
6337           }
6338         }
6339       } else {
6340         for (k = 0; k < dof; ++k) {
6341           if ((cind < cdof) && (k == cdofs[cind])) {
6342             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6343             cind++;
6344           }
6345         }
6346       }
6347     } else {
6348       if (perm) {
6349         for (k = 0; k < dof; ++k) {
6350           if ((cind < cdof) && (k == cdofs[cind])) {
6351             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6352             cind++;
6353           }
6354         }
6355       } else {
6356         for (k = 0; k < dof; ++k) {
6357           if ((cind < cdof) && (k == cdofs[cind])) {
6358             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6359             cind++;
6360           }
6361         }
6362       }
6363     }
6364   }
6365   PetscFunctionReturn(PETSC_SUCCESS);
6366 }
6367 
6368 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[])
6369 {
6370   PetscScalar    *a;
6371   PetscInt        fdof, foff, fcdof, foffset = *offset;
6372   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6373   PetscInt        cind = 0, b;
6374 
6375   PetscFunctionBegin;
6376   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6377   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6378   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6379   a = &array[foff];
6380   if (!fcdof || setBC) {
6381     if (clperm) {
6382       if (perm) {
6383         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6384       } else {
6385         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6386       }
6387     } else {
6388       if (perm) {
6389         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6390       } else {
6391         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6392       }
6393     }
6394   } else {
6395     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6396     if (clperm) {
6397       if (perm) {
6398         for (b = 0; b < fdof; b++) {
6399           if ((cind < fcdof) && (b == fcdofs[cind])) {
6400             ++cind;
6401             continue;
6402           }
6403           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6404         }
6405       } else {
6406         for (b = 0; b < fdof; b++) {
6407           if ((cind < fcdof) && (b == fcdofs[cind])) {
6408             ++cind;
6409             continue;
6410           }
6411           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6412         }
6413       }
6414     } else {
6415       if (perm) {
6416         for (b = 0; b < fdof; b++) {
6417           if ((cind < fcdof) && (b == fcdofs[cind])) {
6418             ++cind;
6419             continue;
6420           }
6421           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6422         }
6423       } else {
6424         for (b = 0; b < fdof; b++) {
6425           if ((cind < fcdof) && (b == fcdofs[cind])) {
6426             ++cind;
6427             continue;
6428           }
6429           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6430         }
6431       }
6432     }
6433   }
6434   *offset += fdof;
6435   PetscFunctionReturn(PETSC_SUCCESS);
6436 }
6437 
6438 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[])
6439 {
6440   PetscScalar    *a;
6441   PetscInt        fdof, foff, fcdof, foffset = *offset;
6442   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6443   PetscInt        Nc, cind = 0, ncind = 0, b;
6444   PetscBool       ncSet, fcSet;
6445 
6446   PetscFunctionBegin;
6447   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6448   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6449   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6450   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6451   a = &array[foff];
6452   if (fcdof) {
6453     /* We just override fcdof and fcdofs with Ncc and comps */
6454     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6455     if (clperm) {
6456       if (perm) {
6457         if (comps) {
6458           for (b = 0; b < fdof; b++) {
6459             ncSet = fcSet = PETSC_FALSE;
6460             if (b % Nc == comps[ncind]) {
6461               ncind = (ncind + 1) % Ncc;
6462               ncSet = PETSC_TRUE;
6463             }
6464             if ((cind < fcdof) && (b == fcdofs[cind])) {
6465               ++cind;
6466               fcSet = PETSC_TRUE;
6467             }
6468             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6469           }
6470         } else {
6471           for (b = 0; b < fdof; b++) {
6472             if ((cind < fcdof) && (b == fcdofs[cind])) {
6473               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6474               ++cind;
6475             }
6476           }
6477         }
6478       } else {
6479         if (comps) {
6480           for (b = 0; b < fdof; b++) {
6481             ncSet = fcSet = PETSC_FALSE;
6482             if (b % Nc == comps[ncind]) {
6483               ncind = (ncind + 1) % Ncc;
6484               ncSet = PETSC_TRUE;
6485             }
6486             if ((cind < fcdof) && (b == fcdofs[cind])) {
6487               ++cind;
6488               fcSet = PETSC_TRUE;
6489             }
6490             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6491           }
6492         } else {
6493           for (b = 0; b < fdof; b++) {
6494             if ((cind < fcdof) && (b == fcdofs[cind])) {
6495               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6496               ++cind;
6497             }
6498           }
6499         }
6500       }
6501     } else {
6502       if (perm) {
6503         if (comps) {
6504           for (b = 0; b < fdof; b++) {
6505             ncSet = fcSet = PETSC_FALSE;
6506             if (b % Nc == comps[ncind]) {
6507               ncind = (ncind + 1) % Ncc;
6508               ncSet = PETSC_TRUE;
6509             }
6510             if ((cind < fcdof) && (b == fcdofs[cind])) {
6511               ++cind;
6512               fcSet = PETSC_TRUE;
6513             }
6514             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6515           }
6516         } else {
6517           for (b = 0; b < fdof; b++) {
6518             if ((cind < fcdof) && (b == fcdofs[cind])) {
6519               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6520               ++cind;
6521             }
6522           }
6523         }
6524       } else {
6525         if (comps) {
6526           for (b = 0; b < fdof; b++) {
6527             ncSet = fcSet = PETSC_FALSE;
6528             if (b % Nc == comps[ncind]) {
6529               ncind = (ncind + 1) % Ncc;
6530               ncSet = PETSC_TRUE;
6531             }
6532             if ((cind < fcdof) && (b == fcdofs[cind])) {
6533               ++cind;
6534               fcSet = PETSC_TRUE;
6535             }
6536             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6537           }
6538         } else {
6539           for (b = 0; b < fdof; b++) {
6540             if ((cind < fcdof) && (b == fcdofs[cind])) {
6541               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6542               ++cind;
6543             }
6544           }
6545         }
6546       }
6547     }
6548   }
6549   *offset += fdof;
6550   PetscFunctionReturn(PETSC_SUCCESS);
6551 }
6552 
6553 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6554 {
6555   PetscScalar    *array;
6556   const PetscInt *cone, *coneO;
6557   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6558 
6559   PetscFunctionBeginHot;
6560   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6561   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6562   PetscCall(DMPlexGetCone(dm, point, &cone));
6563   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6564   PetscCall(VecGetArray(v, &array));
6565   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6566     const PetscInt cp = !p ? point : cone[p - 1];
6567     const PetscInt o  = !p ? 0 : coneO[p - 1];
6568 
6569     if ((cp < pStart) || (cp >= pEnd)) {
6570       dof = 0;
6571       continue;
6572     }
6573     PetscCall(PetscSectionGetDof(section, cp, &dof));
6574     /* ADD_VALUES */
6575     {
6576       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6577       PetscScalar    *a;
6578       PetscInt        cdof, coff, cind = 0, k;
6579 
6580       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6581       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6582       a = &array[coff];
6583       if (!cdof) {
6584         if (o >= 0) {
6585           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6586         } else {
6587           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6588         }
6589       } else {
6590         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6591         if (o >= 0) {
6592           for (k = 0; k < dof; ++k) {
6593             if ((cind < cdof) && (k == cdofs[cind])) {
6594               ++cind;
6595               continue;
6596             }
6597             a[k] += values[off + k];
6598           }
6599         } else {
6600           for (k = 0; k < dof; ++k) {
6601             if ((cind < cdof) && (k == cdofs[cind])) {
6602               ++cind;
6603               continue;
6604             }
6605             a[k] += values[off + dof - k - 1];
6606           }
6607         }
6608       }
6609     }
6610   }
6611   PetscCall(VecRestoreArray(v, &array));
6612   PetscFunctionReturn(PETSC_SUCCESS);
6613 }
6614 
6615 /*@C
6616   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6617 
6618   Not collective
6619 
6620   Input Parameters:
6621 + dm - The `DM`
6622 . section - The section describing the layout in `v`, or `NULL` to use the default section
6623 . v - The local vector
6624 . point - The point in the `DM`
6625 . values - The array of values
6626 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6627          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6628 
6629   Level: intermediate
6630 
6631 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6632 @*/
6633 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6634 {
6635   PetscSection    clSection;
6636   IS              clPoints;
6637   PetscScalar    *array;
6638   PetscInt       *points = NULL;
6639   const PetscInt *clp, *clperm = NULL;
6640   PetscInt        depth, numFields, numPoints, p, clsize;
6641 
6642   PetscFunctionBeginHot;
6643   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6644   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6645   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6646   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6647   PetscCall(DMPlexGetDepth(dm, &depth));
6648   PetscCall(PetscSectionGetNumFields(section, &numFields));
6649   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6650     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6651     PetscFunctionReturn(PETSC_SUCCESS);
6652   }
6653   /* Get points */
6654   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6655   for (clsize = 0, p = 0; p < numPoints; p++) {
6656     PetscInt dof;
6657     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6658     clsize += dof;
6659   }
6660   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6661   /* Get array */
6662   PetscCall(VecGetArray(v, &array));
6663   /* Get values */
6664   if (numFields > 0) {
6665     PetscInt offset = 0, f;
6666     for (f = 0; f < numFields; ++f) {
6667       const PetscInt    **perms = NULL;
6668       const PetscScalar **flips = NULL;
6669 
6670       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6671       switch (mode) {
6672       case INSERT_VALUES:
6673         for (p = 0; p < numPoints; p++) {
6674           const PetscInt     point = points[2 * p];
6675           const PetscInt    *perm  = perms ? perms[p] : NULL;
6676           const PetscScalar *flip  = flips ? flips[p] : NULL;
6677           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6678         }
6679         break;
6680       case INSERT_ALL_VALUES:
6681         for (p = 0; p < numPoints; p++) {
6682           const PetscInt     point = points[2 * p];
6683           const PetscInt    *perm  = perms ? perms[p] : NULL;
6684           const PetscScalar *flip  = flips ? flips[p] : NULL;
6685           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6686         }
6687         break;
6688       case INSERT_BC_VALUES:
6689         for (p = 0; p < numPoints; p++) {
6690           const PetscInt     point = points[2 * p];
6691           const PetscInt    *perm  = perms ? perms[p] : NULL;
6692           const PetscScalar *flip  = flips ? flips[p] : NULL;
6693           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6694         }
6695         break;
6696       case ADD_VALUES:
6697         for (p = 0; p < numPoints; p++) {
6698           const PetscInt     point = points[2 * p];
6699           const PetscInt    *perm  = perms ? perms[p] : NULL;
6700           const PetscScalar *flip  = flips ? flips[p] : NULL;
6701           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6702         }
6703         break;
6704       case ADD_ALL_VALUES:
6705         for (p = 0; p < numPoints; p++) {
6706           const PetscInt     point = points[2 * p];
6707           const PetscInt    *perm  = perms ? perms[p] : NULL;
6708           const PetscScalar *flip  = flips ? flips[p] : NULL;
6709           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6710         }
6711         break;
6712       case ADD_BC_VALUES:
6713         for (p = 0; p < numPoints; p++) {
6714           const PetscInt     point = points[2 * p];
6715           const PetscInt    *perm  = perms ? perms[p] : NULL;
6716           const PetscScalar *flip  = flips ? flips[p] : NULL;
6717           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6718         }
6719         break;
6720       default:
6721         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6722       }
6723       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6724     }
6725   } else {
6726     PetscInt            dof, off;
6727     const PetscInt    **perms = NULL;
6728     const PetscScalar **flips = NULL;
6729 
6730     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6731     switch (mode) {
6732     case INSERT_VALUES:
6733       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6734         const PetscInt     point = points[2 * p];
6735         const PetscInt    *perm  = perms ? perms[p] : NULL;
6736         const PetscScalar *flip  = flips ? flips[p] : NULL;
6737         PetscCall(PetscSectionGetDof(section, point, &dof));
6738         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6739       }
6740       break;
6741     case INSERT_ALL_VALUES:
6742       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6743         const PetscInt     point = points[2 * p];
6744         const PetscInt    *perm  = perms ? perms[p] : NULL;
6745         const PetscScalar *flip  = flips ? flips[p] : NULL;
6746         PetscCall(PetscSectionGetDof(section, point, &dof));
6747         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6748       }
6749       break;
6750     case INSERT_BC_VALUES:
6751       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6752         const PetscInt     point = points[2 * p];
6753         const PetscInt    *perm  = perms ? perms[p] : NULL;
6754         const PetscScalar *flip  = flips ? flips[p] : NULL;
6755         PetscCall(PetscSectionGetDof(section, point, &dof));
6756         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6757       }
6758       break;
6759     case ADD_VALUES:
6760       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6761         const PetscInt     point = points[2 * p];
6762         const PetscInt    *perm  = perms ? perms[p] : NULL;
6763         const PetscScalar *flip  = flips ? flips[p] : NULL;
6764         PetscCall(PetscSectionGetDof(section, point, &dof));
6765         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6766       }
6767       break;
6768     case ADD_ALL_VALUES:
6769       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6770         const PetscInt     point = points[2 * p];
6771         const PetscInt    *perm  = perms ? perms[p] : NULL;
6772         const PetscScalar *flip  = flips ? flips[p] : NULL;
6773         PetscCall(PetscSectionGetDof(section, point, &dof));
6774         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6775       }
6776       break;
6777     case ADD_BC_VALUES:
6778       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6779         const PetscInt     point = points[2 * p];
6780         const PetscInt    *perm  = perms ? perms[p] : NULL;
6781         const PetscScalar *flip  = flips ? flips[p] : NULL;
6782         PetscCall(PetscSectionGetDof(section, point, &dof));
6783         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6784       }
6785       break;
6786     default:
6787       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6788     }
6789     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6790   }
6791   /* Cleanup points */
6792   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6793   /* Cleanup array */
6794   PetscCall(VecRestoreArray(v, &array));
6795   PetscFunctionReturn(PETSC_SUCCESS);
6796 }
6797 
6798 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6799 {
6800   const PetscInt *supp, *cone;
6801   PetscScalar    *a;
6802   PetscInt        dim, Ns, dof, off, n = 0;
6803 
6804   PetscFunctionBegin;
6805   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6806   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6807   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6808   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6809   if (PetscDefined(USE_DEBUG)) {
6810     PetscInt vStart, vEnd;
6811 
6812     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6813     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);
6814   }
6815   PetscValidScalarPointer(values, 5);
6816 
6817   PetscCall(DMGetDimension(dm, &dim));
6818   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6819   PetscCall(DMPlexGetSupport(dm, point, &supp));
6820   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);
6821   PetscCall(VecGetArray(v, &a));
6822   PetscCall(PetscSectionGetDof(section, point, &dof));
6823   PetscCall(PetscSectionGetOffset(section, point, &off));
6824   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6825   for (PetscInt d = 0; d < dim; ++d) {
6826     // Left edge
6827     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6828     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6829     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6830     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6831     // Right edge
6832     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6833     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6834     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6835     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6836   }
6837   PetscCall(VecRestoreArray(v, &a));
6838   PetscFunctionReturn(PETSC_SUCCESS);
6839 }
6840 
6841 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6842 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6843 {
6844   PetscFunctionBegin;
6845   *contains = PETSC_TRUE;
6846   if (label) {
6847     PetscInt fdof;
6848 
6849     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6850     if (!*contains) {
6851       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6852       *offset += fdof;
6853       PetscFunctionReturn(PETSC_SUCCESS);
6854     }
6855   }
6856   PetscFunctionReturn(PETSC_SUCCESS);
6857 }
6858 
6859 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6860 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)
6861 {
6862   PetscSection    clSection;
6863   IS              clPoints;
6864   PetscScalar    *array;
6865   PetscInt       *points = NULL;
6866   const PetscInt *clp;
6867   PetscInt        numFields, numPoints, p;
6868   PetscInt        offset = 0, f;
6869 
6870   PetscFunctionBeginHot;
6871   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6872   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6873   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6874   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6875   PetscCall(PetscSectionGetNumFields(section, &numFields));
6876   /* Get points */
6877   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6878   /* Get array */
6879   PetscCall(VecGetArray(v, &array));
6880   /* Get values */
6881   for (f = 0; f < numFields; ++f) {
6882     const PetscInt    **perms = NULL;
6883     const PetscScalar **flips = NULL;
6884     PetscBool           contains;
6885 
6886     if (!fieldActive[f]) {
6887       for (p = 0; p < numPoints * 2; p += 2) {
6888         PetscInt fdof;
6889         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6890         offset += fdof;
6891       }
6892       continue;
6893     }
6894     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6895     switch (mode) {
6896     case INSERT_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, insert, PETSC_FALSE, NULL, values, &offset, array));
6904       }
6905       break;
6906     case INSERT_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, insert, PETSC_TRUE, NULL, values, &offset, array));
6914       }
6915       break;
6916     case INSERT_BC_VALUES:
6917       for (p = 0; p < numPoints; p++) {
6918         const PetscInt     point = points[2 * p];
6919         const PetscInt    *perm  = perms ? perms[p] : NULL;
6920         const PetscScalar *flip  = flips ? flips[p] : NULL;
6921         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6922         if (!contains) continue;
6923         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6924       }
6925       break;
6926     case ADD_VALUES:
6927       for (p = 0; p < numPoints; p++) {
6928         const PetscInt     point = points[2 * p];
6929         const PetscInt    *perm  = perms ? perms[p] : NULL;
6930         const PetscScalar *flip  = flips ? flips[p] : NULL;
6931         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6932         if (!contains) continue;
6933         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6934       }
6935       break;
6936     case ADD_ALL_VALUES:
6937       for (p = 0; p < numPoints; p++) {
6938         const PetscInt     point = points[2 * p];
6939         const PetscInt    *perm  = perms ? perms[p] : NULL;
6940         const PetscScalar *flip  = flips ? flips[p] : NULL;
6941         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6942         if (!contains) continue;
6943         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6944       }
6945       break;
6946     default:
6947       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6948     }
6949     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6950   }
6951   /* Cleanup points */
6952   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6953   /* Cleanup array */
6954   PetscCall(VecRestoreArray(v, &array));
6955   PetscFunctionReturn(PETSC_SUCCESS);
6956 }
6957 
6958 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6959 {
6960   PetscMPIInt rank;
6961   PetscInt    i, j;
6962 
6963   PetscFunctionBegin;
6964   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6965   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6966   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6967   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6968   numCIndices = numCIndices ? numCIndices : numRIndices;
6969   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6970   for (i = 0; i < numRIndices; i++) {
6971     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6972     for (j = 0; j < numCIndices; j++) {
6973 #if defined(PETSC_USE_COMPLEX)
6974       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6975 #else
6976       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6977 #endif
6978     }
6979     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6980   }
6981   PetscFunctionReturn(PETSC_SUCCESS);
6982 }
6983 
6984 /*
6985   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6986 
6987   Input Parameters:
6988 + section - The section for this data layout
6989 . islocal - Is the section (and thus indices being requested) local or global?
6990 . point   - The point contributing dofs with these indices
6991 . off     - The global offset of this point
6992 . loff    - The local offset of each field
6993 . setBC   - The flag determining whether to include indices of boundary values
6994 . perm    - A permutation of the dofs on this point, or NULL
6995 - indperm - A permutation of the entire indices array, or NULL
6996 
6997   Output Parameter:
6998 . indices - Indices for dofs on this point
6999 
7000   Level: developer
7001 
7002   Note: The indices could be local or global, depending on the value of 'off'.
7003 */
7004 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7005 {
7006   PetscInt        dof;   /* The number of unknowns on this point */
7007   PetscInt        cdof;  /* The number of constraints on this point */
7008   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7009   PetscInt        cind = 0, k;
7010 
7011   PetscFunctionBegin;
7012   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7013   PetscCall(PetscSectionGetDof(section, point, &dof));
7014   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7015   if (!cdof || setBC) {
7016     for (k = 0; k < dof; ++k) {
7017       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7018       const PetscInt ind    = indperm ? indperm[preind] : preind;
7019 
7020       indices[ind] = off + k;
7021     }
7022   } else {
7023     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7024     for (k = 0; k < dof; ++k) {
7025       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7026       const PetscInt ind    = indperm ? indperm[preind] : preind;
7027 
7028       if ((cind < cdof) && (k == cdofs[cind])) {
7029         /* Insert check for returning constrained indices */
7030         indices[ind] = -(off + k + 1);
7031         ++cind;
7032       } else {
7033         indices[ind] = off + k - (islocal ? 0 : cind);
7034       }
7035     }
7036   }
7037   *loff += dof;
7038   PetscFunctionReturn(PETSC_SUCCESS);
7039 }
7040 
7041 /*
7042  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7043 
7044  Input Parameters:
7045 + section - a section (global or local)
7046 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7047 . point - point within section
7048 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7049 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7050 . setBC - identify constrained (boundary condition) points via involution.
7051 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7052 . permsoff - offset
7053 - indperm - index permutation
7054 
7055  Output Parameter:
7056 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7057 . indices - array to hold indices (as defined by section) of each dof associated with point
7058 
7059  Notes:
7060  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7061  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7062  in the local vector.
7063 
7064  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7065  significant).  It is invalid to call with a global section and setBC=true.
7066 
7067  Developer Note:
7068  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7069  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7070  offset could be obtained from the section instead of passing it explicitly as we do now.
7071 
7072  Example:
7073  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7074  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7075  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7076  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.
7077 
7078  Level: developer
7079 */
7080 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[])
7081 {
7082   PetscInt numFields, foff, f;
7083 
7084   PetscFunctionBegin;
7085   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7086   PetscCall(PetscSectionGetNumFields(section, &numFields));
7087   for (f = 0, foff = 0; f < numFields; ++f) {
7088     PetscInt        fdof, cfdof;
7089     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7090     PetscInt        cind = 0, b;
7091     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7092 
7093     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7094     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7095     if (!cfdof || setBC) {
7096       for (b = 0; b < fdof; ++b) {
7097         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7098         const PetscInt ind    = indperm ? indperm[preind] : preind;
7099 
7100         indices[ind] = off + foff + b;
7101       }
7102     } else {
7103       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7104       for (b = 0; b < fdof; ++b) {
7105         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7106         const PetscInt ind    = indperm ? indperm[preind] : preind;
7107 
7108         if ((cind < cfdof) && (b == fcdofs[cind])) {
7109           indices[ind] = -(off + foff + b + 1);
7110           ++cind;
7111         } else {
7112           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7113         }
7114       }
7115     }
7116     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7117     foffs[f] += fdof;
7118   }
7119   PetscFunctionReturn(PETSC_SUCCESS);
7120 }
7121 
7122 /*
7123   This version believes the globalSection offsets for each field, rather than just the point offset
7124 
7125  . foffs - The offset into 'indices' for each field, since it is segregated by field
7126 
7127  Notes:
7128  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7129  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7130 */
7131 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7132 {
7133   PetscInt numFields, foff, f;
7134 
7135   PetscFunctionBegin;
7136   PetscCall(PetscSectionGetNumFields(section, &numFields));
7137   for (f = 0; f < numFields; ++f) {
7138     PetscInt        fdof, cfdof;
7139     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7140     PetscInt        cind = 0, b;
7141     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7142 
7143     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7144     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7145     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7146     if (!cfdof) {
7147       for (b = 0; b < fdof; ++b) {
7148         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7149         const PetscInt ind    = indperm ? indperm[preind] : preind;
7150 
7151         indices[ind] = foff + b;
7152       }
7153     } else {
7154       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7155       for (b = 0; b < fdof; ++b) {
7156         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7157         const PetscInt ind    = indperm ? indperm[preind] : preind;
7158 
7159         if ((cind < cfdof) && (b == fcdofs[cind])) {
7160           indices[ind] = -(foff + b + 1);
7161           ++cind;
7162         } else {
7163           indices[ind] = foff + b - cind;
7164         }
7165       }
7166     }
7167     foffs[f] += fdof;
7168   }
7169   PetscFunctionReturn(PETSC_SUCCESS);
7170 }
7171 
7172 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)
7173 {
7174   Mat             cMat;
7175   PetscSection    aSec, cSec;
7176   IS              aIS;
7177   PetscInt        aStart = -1, aEnd = -1;
7178   const PetscInt *anchors;
7179   PetscInt        numFields, f, p, q, newP = 0;
7180   PetscInt        newNumPoints = 0, newNumIndices = 0;
7181   PetscInt       *newPoints, *indices, *newIndices;
7182   PetscInt        maxAnchor, maxDof;
7183   PetscInt        newOffsets[32];
7184   PetscInt       *pointMatOffsets[32];
7185   PetscInt       *newPointOffsets[32];
7186   PetscScalar    *pointMat[32];
7187   PetscScalar    *newValues      = NULL, *tmpValues;
7188   PetscBool       anyConstrained = PETSC_FALSE;
7189 
7190   PetscFunctionBegin;
7191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7192   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7193   PetscCall(PetscSectionGetNumFields(section, &numFields));
7194 
7195   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7196   /* if there are point-to-point constraints */
7197   if (aSec) {
7198     PetscCall(PetscArrayzero(newOffsets, 32));
7199     PetscCall(ISGetIndices(aIS, &anchors));
7200     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7201     /* figure out how many points are going to be in the new element matrix
7202      * (we allow double counting, because it's all just going to be summed
7203      * into the global matrix anyway) */
7204     for (p = 0; p < 2 * numPoints; p += 2) {
7205       PetscInt b    = points[p];
7206       PetscInt bDof = 0, bSecDof;
7207 
7208       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7209       if (!bSecDof) continue;
7210       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7211       if (bDof) {
7212         /* this point is constrained */
7213         /* it is going to be replaced by its anchors */
7214         PetscInt bOff, q;
7215 
7216         anyConstrained = PETSC_TRUE;
7217         newNumPoints += bDof;
7218         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7219         for (q = 0; q < bDof; q++) {
7220           PetscInt a = anchors[bOff + q];
7221           PetscInt aDof;
7222 
7223           PetscCall(PetscSectionGetDof(section, a, &aDof));
7224           newNumIndices += aDof;
7225           for (f = 0; f < numFields; ++f) {
7226             PetscInt fDof;
7227 
7228             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7229             newOffsets[f + 1] += fDof;
7230           }
7231         }
7232       } else {
7233         /* this point is not constrained */
7234         newNumPoints++;
7235         newNumIndices += bSecDof;
7236         for (f = 0; f < numFields; ++f) {
7237           PetscInt fDof;
7238 
7239           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7240           newOffsets[f + 1] += fDof;
7241         }
7242       }
7243     }
7244   }
7245   if (!anyConstrained) {
7246     if (outNumPoints) *outNumPoints = 0;
7247     if (outNumIndices) *outNumIndices = 0;
7248     if (outPoints) *outPoints = NULL;
7249     if (outValues) *outValues = NULL;
7250     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7251     PetscFunctionReturn(PETSC_SUCCESS);
7252   }
7253 
7254   if (outNumPoints) *outNumPoints = newNumPoints;
7255   if (outNumIndices) *outNumIndices = newNumIndices;
7256 
7257   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7258 
7259   if (!outPoints && !outValues) {
7260     if (offsets) {
7261       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7262     }
7263     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7264     PetscFunctionReturn(PETSC_SUCCESS);
7265   }
7266 
7267   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7268 
7269   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7270 
7271   /* workspaces */
7272   if (numFields) {
7273     for (f = 0; f < numFields; f++) {
7274       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7275       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7276     }
7277   } else {
7278     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7279     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7280   }
7281 
7282   /* get workspaces for the point-to-point matrices */
7283   if (numFields) {
7284     PetscInt totalOffset, totalMatOffset;
7285 
7286     for (p = 0; p < numPoints; p++) {
7287       PetscInt b    = points[2 * p];
7288       PetscInt bDof = 0, bSecDof;
7289 
7290       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7291       if (!bSecDof) {
7292         for (f = 0; f < numFields; f++) {
7293           newPointOffsets[f][p + 1] = 0;
7294           pointMatOffsets[f][p + 1] = 0;
7295         }
7296         continue;
7297       }
7298       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7299       if (bDof) {
7300         for (f = 0; f < numFields; f++) {
7301           PetscInt fDof, q, bOff, allFDof = 0;
7302 
7303           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7304           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7305           for (q = 0; q < bDof; q++) {
7306             PetscInt a = anchors[bOff + q];
7307             PetscInt aFDof;
7308 
7309             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7310             allFDof += aFDof;
7311           }
7312           newPointOffsets[f][p + 1] = allFDof;
7313           pointMatOffsets[f][p + 1] = fDof * allFDof;
7314         }
7315       } else {
7316         for (f = 0; f < numFields; f++) {
7317           PetscInt fDof;
7318 
7319           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7320           newPointOffsets[f][p + 1] = fDof;
7321           pointMatOffsets[f][p + 1] = 0;
7322         }
7323       }
7324     }
7325     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7326       newPointOffsets[f][0] = totalOffset;
7327       pointMatOffsets[f][0] = totalMatOffset;
7328       for (p = 0; p < numPoints; p++) {
7329         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7330         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7331       }
7332       totalOffset    = newPointOffsets[f][numPoints];
7333       totalMatOffset = pointMatOffsets[f][numPoints];
7334       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7335     }
7336   } else {
7337     for (p = 0; p < numPoints; p++) {
7338       PetscInt b    = points[2 * p];
7339       PetscInt bDof = 0, bSecDof;
7340 
7341       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7342       if (!bSecDof) {
7343         newPointOffsets[0][p + 1] = 0;
7344         pointMatOffsets[0][p + 1] = 0;
7345         continue;
7346       }
7347       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7348       if (bDof) {
7349         PetscInt bOff, q, allDof = 0;
7350 
7351         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7352         for (q = 0; q < bDof; q++) {
7353           PetscInt a = anchors[bOff + q], aDof;
7354 
7355           PetscCall(PetscSectionGetDof(section, a, &aDof));
7356           allDof += aDof;
7357         }
7358         newPointOffsets[0][p + 1] = allDof;
7359         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7360       } else {
7361         newPointOffsets[0][p + 1] = bSecDof;
7362         pointMatOffsets[0][p + 1] = 0;
7363       }
7364     }
7365     newPointOffsets[0][0] = 0;
7366     pointMatOffsets[0][0] = 0;
7367     for (p = 0; p < numPoints; p++) {
7368       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7369       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7370     }
7371     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7372   }
7373 
7374   /* output arrays */
7375   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7376 
7377   /* get the point-to-point matrices; construct newPoints */
7378   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7379   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7380   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7381   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7382   if (numFields) {
7383     for (p = 0, newP = 0; p < numPoints; p++) {
7384       PetscInt b    = points[2 * p];
7385       PetscInt o    = points[2 * p + 1];
7386       PetscInt bDof = 0, bSecDof;
7387 
7388       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7389       if (!bSecDof) continue;
7390       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7391       if (bDof) {
7392         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7393 
7394         fStart[0] = 0;
7395         fEnd[0]   = 0;
7396         for (f = 0; f < numFields; f++) {
7397           PetscInt fDof;
7398 
7399           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7400           fStart[f + 1] = fStart[f] + fDof;
7401           fEnd[f + 1]   = fStart[f + 1];
7402         }
7403         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7404         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7405 
7406         fAnchorStart[0] = 0;
7407         fAnchorEnd[0]   = 0;
7408         for (f = 0; f < numFields; f++) {
7409           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7410 
7411           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7412           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7413         }
7414         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7415         for (q = 0; q < bDof; q++) {
7416           PetscInt a = anchors[bOff + q], aOff;
7417 
7418           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7419           newPoints[2 * (newP + q)]     = a;
7420           newPoints[2 * (newP + q) + 1] = 0;
7421           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7422           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7423         }
7424         newP += bDof;
7425 
7426         if (outValues) {
7427           /* get the point-to-point submatrix */
7428           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]));
7429         }
7430       } else {
7431         newPoints[2 * newP]     = b;
7432         newPoints[2 * newP + 1] = o;
7433         newP++;
7434       }
7435     }
7436   } else {
7437     for (p = 0; p < numPoints; p++) {
7438       PetscInt b    = points[2 * p];
7439       PetscInt o    = points[2 * p + 1];
7440       PetscInt bDof = 0, bSecDof;
7441 
7442       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7443       if (!bSecDof) continue;
7444       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7445       if (bDof) {
7446         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7447 
7448         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7449         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7450 
7451         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7452         for (q = 0; q < bDof; q++) {
7453           PetscInt a = anchors[bOff + q], aOff;
7454 
7455           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7456 
7457           newPoints[2 * (newP + q)]     = a;
7458           newPoints[2 * (newP + q) + 1] = 0;
7459           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7460           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7461         }
7462         newP += bDof;
7463 
7464         /* get the point-to-point submatrix */
7465         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7466       } else {
7467         newPoints[2 * newP]     = b;
7468         newPoints[2 * newP + 1] = o;
7469         newP++;
7470       }
7471     }
7472   }
7473 
7474   if (outValues) {
7475     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7476     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7477     /* multiply constraints on the right */
7478     if (numFields) {
7479       for (f = 0; f < numFields; f++) {
7480         PetscInt oldOff = offsets[f];
7481 
7482         for (p = 0; p < numPoints; p++) {
7483           PetscInt cStart = newPointOffsets[f][p];
7484           PetscInt b      = points[2 * p];
7485           PetscInt c, r, k;
7486           PetscInt dof;
7487 
7488           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7489           if (!dof) continue;
7490           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7491             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7492             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7493 
7494             for (r = 0; r < numIndices; r++) {
7495               for (c = 0; c < nCols; c++) {
7496                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7497               }
7498             }
7499           } else {
7500             /* copy this column as is */
7501             for (r = 0; r < numIndices; r++) {
7502               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7503             }
7504           }
7505           oldOff += dof;
7506         }
7507       }
7508     } else {
7509       PetscInt oldOff = 0;
7510       for (p = 0; p < numPoints; p++) {
7511         PetscInt cStart = newPointOffsets[0][p];
7512         PetscInt b      = points[2 * p];
7513         PetscInt c, r, k;
7514         PetscInt dof;
7515 
7516         PetscCall(PetscSectionGetDof(section, b, &dof));
7517         if (!dof) continue;
7518         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7519           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7520           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7521 
7522           for (r = 0; r < numIndices; r++) {
7523             for (c = 0; c < nCols; c++) {
7524               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7525             }
7526           }
7527         } else {
7528           /* copy this column as is */
7529           for (r = 0; r < numIndices; r++) {
7530             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7531           }
7532         }
7533         oldOff += dof;
7534       }
7535     }
7536 
7537     if (multiplyLeft) {
7538       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7539       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7540       /* multiply constraints transpose on the left */
7541       if (numFields) {
7542         for (f = 0; f < numFields; f++) {
7543           PetscInt oldOff = offsets[f];
7544 
7545           for (p = 0; p < numPoints; p++) {
7546             PetscInt rStart = newPointOffsets[f][p];
7547             PetscInt b      = points[2 * p];
7548             PetscInt c, r, k;
7549             PetscInt dof;
7550 
7551             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7552             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7553               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7554               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7555 
7556               for (r = 0; r < nRows; r++) {
7557                 for (c = 0; c < newNumIndices; c++) {
7558                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7559                 }
7560               }
7561             } else {
7562               /* copy this row as is */
7563               for (r = 0; r < dof; r++) {
7564                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7565               }
7566             }
7567             oldOff += dof;
7568           }
7569         }
7570       } else {
7571         PetscInt oldOff = 0;
7572 
7573         for (p = 0; p < numPoints; p++) {
7574           PetscInt rStart = newPointOffsets[0][p];
7575           PetscInt b      = points[2 * p];
7576           PetscInt c, r, k;
7577           PetscInt dof;
7578 
7579           PetscCall(PetscSectionGetDof(section, b, &dof));
7580           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7581             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7582             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7583 
7584             for (r = 0; r < nRows; r++) {
7585               for (c = 0; c < newNumIndices; c++) {
7586                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7587               }
7588             }
7589           } else {
7590             /* copy this row as is */
7591             for (r = 0; r < dof; r++) {
7592               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7593             }
7594           }
7595           oldOff += dof;
7596         }
7597       }
7598 
7599       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7600     } else {
7601       newValues = tmpValues;
7602     }
7603   }
7604 
7605   /* clean up */
7606   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7607   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7608 
7609   if (numFields) {
7610     for (f = 0; f < numFields; f++) {
7611       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7612       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7613       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7614     }
7615   } else {
7616     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7617     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7618     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7619   }
7620   PetscCall(ISRestoreIndices(aIS, &anchors));
7621 
7622   /* output */
7623   if (outPoints) {
7624     *outPoints = newPoints;
7625   } else {
7626     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7627   }
7628   if (outValues) *outValues = newValues;
7629   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7630   PetscFunctionReturn(PETSC_SUCCESS);
7631 }
7632 
7633 /*@C
7634   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7635 
7636   Not collective
7637 
7638   Input Parameters:
7639 + dm         - The `DM`
7640 . section    - The `PetscSection` describing the points (a local section)
7641 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7642 . point      - The point defining the closure
7643 - useClPerm  - Use the closure point permutation if available
7644 
7645   Output Parameters:
7646 + numIndices - The number of dof indices in the closure of point with the input sections
7647 . indices    - The dof indices
7648 . outOffsets - Array to write the field offsets into, or `NULL`
7649 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7650 
7651   Level: advanced
7652 
7653   Notes:
7654   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7655 
7656   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7657   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7658   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7659   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7660   indices (with the above semantics) are implied.
7661 
7662 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7663           `PetscSection`, `DMGetGlobalSection()`
7664 @*/
7665 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7666 {
7667   /* Closure ordering */
7668   PetscSection    clSection;
7669   IS              clPoints;
7670   const PetscInt *clp;
7671   PetscInt       *points;
7672   const PetscInt *clperm = NULL;
7673   /* Dof permutation and sign flips */
7674   const PetscInt    **perms[32] = {NULL};
7675   const PetscScalar **flips[32] = {NULL};
7676   PetscScalar        *valCopy   = NULL;
7677   /* Hanging node constraints */
7678   PetscInt    *pointsC = NULL;
7679   PetscScalar *valuesC = NULL;
7680   PetscInt     NclC, NiC;
7681 
7682   PetscInt *idx;
7683   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7684   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7685 
7686   PetscFunctionBeginHot;
7687   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7688   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7689   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7690   if (numIndices) PetscValidIntPointer(numIndices, 6);
7691   if (indices) PetscValidPointer(indices, 7);
7692   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7693   if (values) PetscValidPointer(values, 9);
7694   PetscCall(PetscSectionGetNumFields(section, &Nf));
7695   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7696   PetscCall(PetscArrayzero(offsets, 32));
7697   /* 1) Get points in closure */
7698   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7699   if (useClPerm) {
7700     PetscInt depth, clsize;
7701     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7702     for (clsize = 0, p = 0; p < Ncl; p++) {
7703       PetscInt dof;
7704       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7705       clsize += dof;
7706     }
7707     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7708   }
7709   /* 2) Get number of indices on these points and field offsets from section */
7710   for (p = 0; p < Ncl * 2; p += 2) {
7711     PetscInt dof, fdof;
7712 
7713     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7714     for (f = 0; f < Nf; ++f) {
7715       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7716       offsets[f + 1] += fdof;
7717     }
7718     Ni += dof;
7719   }
7720   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7721   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7722   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7723   for (f = 0; f < PetscMax(1, Nf); ++f) {
7724     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7725     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7726     /* may need to apply sign changes to the element matrix */
7727     if (values && flips[f]) {
7728       PetscInt foffset = offsets[f];
7729 
7730       for (p = 0; p < Ncl; ++p) {
7731         PetscInt           pnt  = points[2 * p], fdof;
7732         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7733 
7734         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7735         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7736         if (flip) {
7737           PetscInt i, j, k;
7738 
7739           if (!valCopy) {
7740             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7741             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7742             *values = valCopy;
7743           }
7744           for (i = 0; i < fdof; ++i) {
7745             PetscScalar fval = flip[i];
7746 
7747             for (k = 0; k < Ni; ++k) {
7748               valCopy[Ni * (foffset + i) + k] *= fval;
7749               valCopy[Ni * k + (foffset + i)] *= fval;
7750             }
7751           }
7752         }
7753         foffset += fdof;
7754       }
7755     }
7756   }
7757   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7758   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7759   if (NclC) {
7760     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7761     for (f = 0; f < PetscMax(1, Nf); ++f) {
7762       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7763       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7764     }
7765     for (f = 0; f < PetscMax(1, Nf); ++f) {
7766       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7767       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7768     }
7769     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7770     Ncl    = NclC;
7771     Ni     = NiC;
7772     points = pointsC;
7773     if (values) *values = valuesC;
7774   }
7775   /* 5) Calculate indices */
7776   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7777   if (Nf) {
7778     PetscInt  idxOff;
7779     PetscBool useFieldOffsets;
7780 
7781     if (outOffsets) {
7782       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7783     }
7784     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7785     if (useFieldOffsets) {
7786       for (p = 0; p < Ncl; ++p) {
7787         const PetscInt pnt = points[p * 2];
7788 
7789         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7790       }
7791     } else {
7792       for (p = 0; p < Ncl; ++p) {
7793         const PetscInt pnt = points[p * 2];
7794 
7795         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7796         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7797          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7798          * global section. */
7799         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7800       }
7801     }
7802   } else {
7803     PetscInt off = 0, idxOff;
7804 
7805     for (p = 0; p < Ncl; ++p) {
7806       const PetscInt  pnt  = points[p * 2];
7807       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7808 
7809       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7810       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7811        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7812       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7813     }
7814   }
7815   /* 6) Cleanup */
7816   for (f = 0; f < PetscMax(1, Nf); ++f) {
7817     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7818     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7819   }
7820   if (NclC) {
7821     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7822   } else {
7823     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7824   }
7825 
7826   if (numIndices) *numIndices = Ni;
7827   if (indices) *indices = idx;
7828   PetscFunctionReturn(PETSC_SUCCESS);
7829 }
7830 
7831 /*@C
7832   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7833 
7834   Not collective
7835 
7836   Input Parameters:
7837 + dm         - The `DM`
7838 . section    - The `PetscSection` describing the points (a local section)
7839 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7840 . point      - The point defining the closure
7841 - useClPerm  - Use the closure point permutation if available
7842 
7843   Output Parameters:
7844 + numIndices - The number of dof indices in the closure of point with the input sections
7845 . indices    - The dof indices
7846 . outOffsets - Array to write the field offsets into, or `NULL`
7847 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7848 
7849   Level: advanced
7850 
7851   Notes:
7852   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7853 
7854   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7855   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7856   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7857   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7858   indices (with the above semantics) are implied.
7859 
7860 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7861 @*/
7862 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7863 {
7864   PetscFunctionBegin;
7865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7866   PetscValidPointer(indices, 7);
7867   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7868   PetscFunctionReturn(PETSC_SUCCESS);
7869 }
7870 
7871 /*@C
7872   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7873 
7874   Not collective
7875 
7876   Input Parameters:
7877 + dm - The `DM`
7878 . section - The section describing the layout in `v`, or `NULL` to use the default section
7879 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7880 . A - The matrix
7881 . point - The point in the `DM`
7882 . values - The array of values
7883 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7884 
7885   Level: intermediate
7886 
7887 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7888 @*/
7889 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7890 {
7891   DM_Plex           *mesh = (DM_Plex *)dm->data;
7892   PetscInt          *indices;
7893   PetscInt           numIndices;
7894   const PetscScalar *valuesOrig = values;
7895   PetscErrorCode     ierr;
7896 
7897   PetscFunctionBegin;
7898   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7899   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7900   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7901   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7902   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7903   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7904 
7905   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7906 
7907   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7908   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7909   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7910   if (ierr) {
7911     PetscMPIInt rank;
7912 
7913     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7914     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7915     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7916     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7917     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7918     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7919   }
7920   if (mesh->printFEM > 1) {
7921     PetscInt i;
7922     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7923     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7924     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7925   }
7926 
7927   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7928   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7929   PetscFunctionReturn(PETSC_SUCCESS);
7930 }
7931 
7932 /*@C
7933   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7934 
7935   Not collective
7936 
7937   Input Parameters:
7938 + dmRow - The `DM` for the row fields
7939 . sectionRow - The section describing the layout, or `NULL` to use the default section in `dmRow`
7940 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7941 . dmCol - The `DM` for the column fields
7942 . sectionCol - The section describing the layout, or `NULL` to use the default section in `dmCol`
7943 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7944 . A - The matrix
7945 . point - The point in the `DM`
7946 . values - The array of values
7947 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7948 
7949   Level: intermediate
7950 
7951 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7952 @*/
7953 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7954 {
7955   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7956   PetscInt          *indicesRow, *indicesCol;
7957   PetscInt           numIndicesRow, numIndicesCol;
7958   const PetscScalar *valuesOrig = values;
7959   PetscErrorCode     ierr;
7960 
7961   PetscFunctionBegin;
7962   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7963   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7964   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7965   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7966   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7967   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7968   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7969   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7970   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7971   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7972   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7973 
7974   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7975   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7976 
7977   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7978   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7979   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7980   if (ierr) {
7981     PetscMPIInt rank;
7982 
7983     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7984     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7985     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7986     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7987     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7988     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7989   }
7990 
7991   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7992   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7993   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7994   PetscFunctionReturn(PETSC_SUCCESS);
7995 }
7996 
7997 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7998 {
7999   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8000   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8001   PetscInt       *cpoints = NULL;
8002   PetscInt       *findices, *cindices;
8003   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8004   PetscInt        foffsets[32], coffsets[32];
8005   DMPolytopeType  ct;
8006   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8007   PetscErrorCode  ierr;
8008 
8009   PetscFunctionBegin;
8010   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8011   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8012   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8013   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8014   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8015   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8016   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8017   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8018   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8019   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8020   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8021   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8022   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8023   PetscCall(PetscArrayzero(foffsets, 32));
8024   PetscCall(PetscArrayzero(coffsets, 32));
8025   /* Column indices */
8026   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8027   maxFPoints = numCPoints;
8028   /* Compress out points not in the section */
8029   /*   TODO: Squeeze out points with 0 dof as well */
8030   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8031   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8032     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8033       cpoints[q * 2]     = cpoints[p];
8034       cpoints[q * 2 + 1] = cpoints[p + 1];
8035       ++q;
8036     }
8037   }
8038   numCPoints = q;
8039   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8040     PetscInt fdof;
8041 
8042     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8043     if (!dof) continue;
8044     for (f = 0; f < numFields; ++f) {
8045       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8046       coffsets[f + 1] += fdof;
8047     }
8048     numCIndices += dof;
8049   }
8050   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8051   /* Row indices */
8052   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8053   {
8054     DMPlexTransform tr;
8055     DMPolytopeType *rct;
8056     PetscInt       *rsize, *rcone, *rornt, Nt;
8057 
8058     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8059     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8060     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8061     numSubcells = rsize[Nt - 1];
8062     PetscCall(DMPlexTransformDestroy(&tr));
8063   }
8064   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8065   for (r = 0, q = 0; r < numSubcells; ++r) {
8066     /* TODO Map from coarse to fine cells */
8067     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8068     /* Compress out points not in the section */
8069     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8070     for (p = 0; p < numFPoints * 2; p += 2) {
8071       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8072         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8073         if (!dof) continue;
8074         for (s = 0; s < q; ++s)
8075           if (fpoints[p] == ftotpoints[s * 2]) break;
8076         if (s < q) continue;
8077         ftotpoints[q * 2]     = fpoints[p];
8078         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8079         ++q;
8080       }
8081     }
8082     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8083   }
8084   numFPoints = q;
8085   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8086     PetscInt fdof;
8087 
8088     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8089     if (!dof) continue;
8090     for (f = 0; f < numFields; ++f) {
8091       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8092       foffsets[f + 1] += fdof;
8093     }
8094     numFIndices += dof;
8095   }
8096   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8097 
8098   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8099   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8100   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8101   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8102   if (numFields) {
8103     const PetscInt **permsF[32] = {NULL};
8104     const PetscInt **permsC[32] = {NULL};
8105 
8106     for (f = 0; f < numFields; f++) {
8107       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8108       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8109     }
8110     for (p = 0; p < numFPoints; p++) {
8111       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8112       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8113     }
8114     for (p = 0; p < numCPoints; p++) {
8115       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8116       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8117     }
8118     for (f = 0; f < numFields; f++) {
8119       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8120       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8121     }
8122   } else {
8123     const PetscInt **permsF = NULL;
8124     const PetscInt **permsC = NULL;
8125 
8126     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8127     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8128     for (p = 0, off = 0; p < numFPoints; p++) {
8129       const PetscInt *perm = permsF ? permsF[p] : NULL;
8130 
8131       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8132       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8133     }
8134     for (p = 0, off = 0; p < numCPoints; p++) {
8135       const PetscInt *perm = permsC ? permsC[p] : NULL;
8136 
8137       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8138       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8139     }
8140     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8141     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8142   }
8143   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8144   /* TODO: flips */
8145   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8146   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8147   if (ierr) {
8148     PetscMPIInt rank;
8149 
8150     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8151     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8152     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8153     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8154     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8155   }
8156   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8157   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8158   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8159   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8160   PetscFunctionReturn(PETSC_SUCCESS);
8161 }
8162 
8163 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8164 {
8165   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8166   PetscInt       *cpoints = NULL;
8167   PetscInt        foffsets[32], coffsets[32];
8168   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8169   DMPolytopeType  ct;
8170   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8171 
8172   PetscFunctionBegin;
8173   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8174   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8175   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8176   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8177   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8178   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8179   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8180   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8181   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8182   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8183   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8184   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8185   PetscCall(PetscArrayzero(foffsets, 32));
8186   PetscCall(PetscArrayzero(coffsets, 32));
8187   /* Column indices */
8188   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8189   maxFPoints = numCPoints;
8190   /* Compress out points not in the section */
8191   /*   TODO: Squeeze out points with 0 dof as well */
8192   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8193   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8194     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8195       cpoints[q * 2]     = cpoints[p];
8196       cpoints[q * 2 + 1] = cpoints[p + 1];
8197       ++q;
8198     }
8199   }
8200   numCPoints = q;
8201   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8202     PetscInt fdof;
8203 
8204     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8205     if (!dof) continue;
8206     for (f = 0; f < numFields; ++f) {
8207       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8208       coffsets[f + 1] += fdof;
8209     }
8210     numCIndices += dof;
8211   }
8212   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8213   /* Row indices */
8214   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8215   {
8216     DMPlexTransform tr;
8217     DMPolytopeType *rct;
8218     PetscInt       *rsize, *rcone, *rornt, Nt;
8219 
8220     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8221     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8222     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8223     numSubcells = rsize[Nt - 1];
8224     PetscCall(DMPlexTransformDestroy(&tr));
8225   }
8226   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8227   for (r = 0, q = 0; r < numSubcells; ++r) {
8228     /* TODO Map from coarse to fine cells */
8229     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8230     /* Compress out points not in the section */
8231     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8232     for (p = 0; p < numFPoints * 2; p += 2) {
8233       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8234         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8235         if (!dof) continue;
8236         for (s = 0; s < q; ++s)
8237           if (fpoints[p] == ftotpoints[s * 2]) break;
8238         if (s < q) continue;
8239         ftotpoints[q * 2]     = fpoints[p];
8240         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8241         ++q;
8242       }
8243     }
8244     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8245   }
8246   numFPoints = q;
8247   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8248     PetscInt fdof;
8249 
8250     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8251     if (!dof) continue;
8252     for (f = 0; f < numFields; ++f) {
8253       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8254       foffsets[f + 1] += fdof;
8255     }
8256     numFIndices += dof;
8257   }
8258   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8259 
8260   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8261   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8262   if (numFields) {
8263     const PetscInt **permsF[32] = {NULL};
8264     const PetscInt **permsC[32] = {NULL};
8265 
8266     for (f = 0; f < numFields; f++) {
8267       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8268       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8269     }
8270     for (p = 0; p < numFPoints; p++) {
8271       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8272       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8273     }
8274     for (p = 0; p < numCPoints; p++) {
8275       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8276       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8277     }
8278     for (f = 0; f < numFields; f++) {
8279       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8280       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8281     }
8282   } else {
8283     const PetscInt **permsF = NULL;
8284     const PetscInt **permsC = NULL;
8285 
8286     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8287     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8288     for (p = 0, off = 0; p < numFPoints; p++) {
8289       const PetscInt *perm = permsF ? permsF[p] : NULL;
8290 
8291       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8292       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8293     }
8294     for (p = 0, off = 0; p < numCPoints; p++) {
8295       const PetscInt *perm = permsC ? permsC[p] : NULL;
8296 
8297       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8298       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8299     }
8300     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8301     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8302   }
8303   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8304   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8305   PetscFunctionReturn(PETSC_SUCCESS);
8306 }
8307 
8308 /*@C
8309   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8310 
8311   Input Parameter:
8312 . dm   - The `DMPLEX` object
8313 
8314   Output Parameter:
8315 . cellHeight - The height of a cell
8316 
8317   Level: developer
8318 
8319 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8320 @*/
8321 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8322 {
8323   DM_Plex *mesh = (DM_Plex *)dm->data;
8324 
8325   PetscFunctionBegin;
8326   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8327   PetscValidIntPointer(cellHeight, 2);
8328   *cellHeight = mesh->vtkCellHeight;
8329   PetscFunctionReturn(PETSC_SUCCESS);
8330 }
8331 
8332 /*@C
8333   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8334 
8335   Input Parameters:
8336 + dm   - The `DMPLEX` object
8337 - cellHeight - The height of a cell
8338 
8339   Level: developer
8340 
8341 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8342 @*/
8343 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8344 {
8345   DM_Plex *mesh = (DM_Plex *)dm->data;
8346 
8347   PetscFunctionBegin;
8348   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8349   mesh->vtkCellHeight = cellHeight;
8350   PetscFunctionReturn(PETSC_SUCCESS);
8351 }
8352 
8353 /*@
8354   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8355 
8356   Input Parameters:
8357 + dm - The `DMPLEX` object
8358 - ct - The `DMPolytopeType` of the cell
8359 
8360   Output Parameters:
8361 + start - The first cell of this type, or `NULL`
8362 - end   - The upper bound on this celltype, or `NULL`
8363 
8364   Level: advanced
8365 
8366 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8367 @*/
8368 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8369 {
8370   DM_Plex *mesh = (DM_Plex *)dm->data;
8371   DMLabel  label;
8372   PetscInt pStart, pEnd;
8373 
8374   PetscFunctionBegin;
8375   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8376   if (start) {
8377     PetscValidIntPointer(start, 3);
8378     *start = 0;
8379   }
8380   if (end) {
8381     PetscValidIntPointer(end, 4);
8382     *end = 0;
8383   }
8384   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8385   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8386   if (mesh->tr) {
8387     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8388   } else {
8389     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8390     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8391     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8392   }
8393   PetscFunctionReturn(PETSC_SUCCESS);
8394 }
8395 
8396 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8397 {
8398   PetscSection section, globalSection;
8399   PetscInt    *numbers, p;
8400 
8401   PetscFunctionBegin;
8402   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8403   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8404   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8405   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8406   PetscCall(PetscSectionSetUp(section));
8407   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8408   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8409   for (p = pStart; p < pEnd; ++p) {
8410     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8411     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8412     else numbers[p - pStart] += shift;
8413   }
8414   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8415   if (globalSize) {
8416     PetscLayout layout;
8417     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8418     PetscCall(PetscLayoutGetSize(layout, globalSize));
8419     PetscCall(PetscLayoutDestroy(&layout));
8420   }
8421   PetscCall(PetscSectionDestroy(&section));
8422   PetscCall(PetscSectionDestroy(&globalSection));
8423   PetscFunctionReturn(PETSC_SUCCESS);
8424 }
8425 
8426 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8427 {
8428   PetscInt cellHeight, cStart, cEnd;
8429 
8430   PetscFunctionBegin;
8431   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8432   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8433   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8434   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8435   PetscFunctionReturn(PETSC_SUCCESS);
8436 }
8437 
8438 /*@
8439   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8440 
8441   Input Parameter:
8442 . dm   - The `DMPLEX` object
8443 
8444   Output Parameter:
8445 . globalCellNumbers - Global cell numbers for all cells on this process
8446 
8447   Level: developer
8448 
8449 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8450 @*/
8451 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8452 {
8453   DM_Plex *mesh = (DM_Plex *)dm->data;
8454 
8455   PetscFunctionBegin;
8456   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8457   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8458   *globalCellNumbers = mesh->globalCellNumbers;
8459   PetscFunctionReturn(PETSC_SUCCESS);
8460 }
8461 
8462 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8463 {
8464   PetscInt vStart, vEnd;
8465 
8466   PetscFunctionBegin;
8467   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8468   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8469   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8470   PetscFunctionReturn(PETSC_SUCCESS);
8471 }
8472 
8473 /*@
8474   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8475 
8476   Input Parameter:
8477 . dm   - The `DMPLEX` object
8478 
8479   Output Parameter:
8480 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8481 
8482   Level: developer
8483 
8484 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8485 @*/
8486 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8487 {
8488   DM_Plex *mesh = (DM_Plex *)dm->data;
8489 
8490   PetscFunctionBegin;
8491   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8492   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8493   *globalVertexNumbers = mesh->globalVertexNumbers;
8494   PetscFunctionReturn(PETSC_SUCCESS);
8495 }
8496 
8497 /*@
8498   DMPlexCreatePointNumbering - Create a global numbering for all points.
8499 
8500   Collective
8501 
8502   Input Parameter:
8503 . dm   - The `DMPLEX` object
8504 
8505   Output Parameter:
8506 . globalPointNumbers - Global numbers for all points on this process
8507 
8508   Level: developer
8509 
8510   Notes:
8511   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8512   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8513   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8514   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8515 
8516   The partitioned mesh is
8517 ```
8518  (2)--0--(3)--1--(4)    (1)--0--(2)
8519 ```
8520   and its global numbering is
8521 ```
8522   (3)--0--(4)--1--(5)--2--(6)
8523 ```
8524   Then the global numbering is provided as
8525 ```
8526 [0] Number of indices in set 5
8527 [0] 0 0
8528 [0] 1 1
8529 [0] 2 3
8530 [0] 3 4
8531 [0] 4 -6
8532 [1] Number of indices in set 3
8533 [1] 0 2
8534 [1] 1 5
8535 [1] 2 6
8536 ```
8537 
8538 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8539 @*/
8540 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8541 {
8542   IS        nums[4];
8543   PetscInt  depths[4], gdepths[4], starts[4];
8544   PetscInt  depth, d, shift = 0;
8545   PetscBool empty = PETSC_FALSE;
8546 
8547   PetscFunctionBegin;
8548   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8549   PetscCall(DMPlexGetDepth(dm, &depth));
8550   // For unstratified meshes use dim instead of depth
8551   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8552   // If any stratum is empty, we must mark all empty
8553   for (d = 0; d <= depth; ++d) {
8554     PetscInt end;
8555 
8556     depths[d] = depth - d;
8557     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8558     if (!(starts[d] - end)) empty = PETSC_TRUE;
8559   }
8560   if (empty)
8561     for (d = 0; d <= depth; ++d) {
8562       depths[d] = -1;
8563       starts[d] = -1;
8564     }
8565   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8566   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8567   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]);
8568   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8569   for (d = 0; d <= depth; ++d) {
8570     PetscInt pStart, pEnd, gsize;
8571 
8572     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8573     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8574     shift += gsize;
8575   }
8576   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8577   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8578   PetscFunctionReturn(PETSC_SUCCESS);
8579 }
8580 
8581 /*@
8582   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8583 
8584   Input Parameter:
8585 . dm - The `DMPLEX` object
8586 
8587   Output Parameter:
8588 . ranks - The rank field
8589 
8590   Options Database Key:
8591 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8592 
8593   Level: intermediate
8594 
8595 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8596 @*/
8597 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8598 {
8599   DM             rdm;
8600   PetscFE        fe;
8601   PetscScalar   *r;
8602   PetscMPIInt    rank;
8603   DMPolytopeType ct;
8604   PetscInt       dim, cStart, cEnd, c;
8605   PetscBool      simplex;
8606 
8607   PetscFunctionBeginUser;
8608   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8609   PetscValidPointer(ranks, 2);
8610   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8611   PetscCall(DMClone(dm, &rdm));
8612   PetscCall(DMGetDimension(rdm, &dim));
8613   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8614   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8615   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8616   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8617   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8618   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8619   PetscCall(PetscFEDestroy(&fe));
8620   PetscCall(DMCreateDS(rdm));
8621   PetscCall(DMCreateGlobalVector(rdm, ranks));
8622   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8623   PetscCall(VecGetArray(*ranks, &r));
8624   for (c = cStart; c < cEnd; ++c) {
8625     PetscScalar *lr;
8626 
8627     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8628     if (lr) *lr = rank;
8629   }
8630   PetscCall(VecRestoreArray(*ranks, &r));
8631   PetscCall(DMDestroy(&rdm));
8632   PetscFunctionReturn(PETSC_SUCCESS);
8633 }
8634 
8635 /*@
8636   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8637 
8638   Input Parameters:
8639 + dm    - The `DMPLEX`
8640 - label - The `DMLabel`
8641 
8642   Output Parameter:
8643 . val - The label value field
8644 
8645   Options Database Key:
8646 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8647 
8648   Level: intermediate
8649 
8650 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8651 @*/
8652 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8653 {
8654   DM           rdm;
8655   PetscFE      fe;
8656   PetscScalar *v;
8657   PetscInt     dim, cStart, cEnd, c;
8658 
8659   PetscFunctionBeginUser;
8660   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8661   PetscValidPointer(label, 2);
8662   PetscValidPointer(val, 3);
8663   PetscCall(DMClone(dm, &rdm));
8664   PetscCall(DMGetDimension(rdm, &dim));
8665   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8666   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8667   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8668   PetscCall(PetscFEDestroy(&fe));
8669   PetscCall(DMCreateDS(rdm));
8670   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8671   PetscCall(DMCreateGlobalVector(rdm, val));
8672   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8673   PetscCall(VecGetArray(*val, &v));
8674   for (c = cStart; c < cEnd; ++c) {
8675     PetscScalar *lv;
8676     PetscInt     cval;
8677 
8678     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8679     PetscCall(DMLabelGetValue(label, c, &cval));
8680     *lv = cval;
8681   }
8682   PetscCall(VecRestoreArray(*val, &v));
8683   PetscCall(DMDestroy(&rdm));
8684   PetscFunctionReturn(PETSC_SUCCESS);
8685 }
8686 
8687 /*@
8688   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8689 
8690   Input Parameter:
8691 . dm - The `DMPLEX` object
8692 
8693   Level: developer
8694 
8695   Notes:
8696   This is a useful diagnostic when creating meshes programmatically.
8697 
8698   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8699 
8700 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8701 @*/
8702 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8703 {
8704   PetscSection    coneSection, supportSection;
8705   const PetscInt *cone, *support;
8706   PetscInt        coneSize, c, supportSize, s;
8707   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8708   PetscBool       storagecheck = PETSC_TRUE;
8709 
8710   PetscFunctionBegin;
8711   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8712   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8713   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8714   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8715   /* Check that point p is found in the support of its cone points, and vice versa */
8716   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8717   for (p = pStart; p < pEnd; ++p) {
8718     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8719     PetscCall(DMPlexGetCone(dm, p, &cone));
8720     for (c = 0; c < coneSize; ++c) {
8721       PetscBool dup = PETSC_FALSE;
8722       PetscInt  d;
8723       for (d = c - 1; d >= 0; --d) {
8724         if (cone[c] == cone[d]) {
8725           dup = PETSC_TRUE;
8726           break;
8727         }
8728       }
8729       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8730       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8731       for (s = 0; s < supportSize; ++s) {
8732         if (support[s] == p) break;
8733       }
8734       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8735         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8736         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8737         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8738         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8739         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8740         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8741         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]);
8742         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8743       }
8744     }
8745     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8746     if (p != pp) {
8747       storagecheck = PETSC_FALSE;
8748       continue;
8749     }
8750     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8751     PetscCall(DMPlexGetSupport(dm, p, &support));
8752     for (s = 0; s < supportSize; ++s) {
8753       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8754       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8755       for (c = 0; c < coneSize; ++c) {
8756         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8757         if (cone[c] != pp) {
8758           c = 0;
8759           break;
8760         }
8761         if (cone[c] == p) break;
8762       }
8763       if (c >= coneSize) {
8764         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8765         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8766         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8767         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8768         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8769         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8770         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8771       }
8772     }
8773   }
8774   if (storagecheck) {
8775     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8776     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8777     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8778   }
8779   PetscFunctionReturn(PETSC_SUCCESS);
8780 }
8781 
8782 /*
8783   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.
8784 */
8785 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8786 {
8787   DMPolytopeType  cct;
8788   PetscInt        ptpoints[4];
8789   const PetscInt *cone, *ccone, *ptcone;
8790   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8791 
8792   PetscFunctionBegin;
8793   *unsplit = 0;
8794   switch (ct) {
8795   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8796     ptpoints[npt++] = c;
8797     break;
8798   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8799     PetscCall(DMPlexGetCone(dm, c, &cone));
8800     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8801     for (cp = 0; cp < coneSize; ++cp) {
8802       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8803       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8804     }
8805     break;
8806   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8807   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8808     PetscCall(DMPlexGetCone(dm, c, &cone));
8809     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8810     for (cp = 0; cp < coneSize; ++cp) {
8811       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8812       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8813       for (ccp = 0; ccp < cconeSize; ++ccp) {
8814         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8815         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8816           PetscInt p;
8817           for (p = 0; p < npt; ++p)
8818             if (ptpoints[p] == ccone[ccp]) break;
8819           if (p == npt) ptpoints[npt++] = ccone[ccp];
8820         }
8821       }
8822     }
8823     break;
8824   default:
8825     break;
8826   }
8827   for (pt = 0; pt < npt; ++pt) {
8828     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8829     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8830   }
8831   PetscFunctionReturn(PETSC_SUCCESS);
8832 }
8833 
8834 /*@
8835   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8836 
8837   Input Parameters:
8838 + dm - The `DMPLEX` object
8839 - cellHeight - Normally 0
8840 
8841   Level: developer
8842 
8843   Notes:
8844   This is a useful diagnostic when creating meshes programmatically.
8845   Currently applicable only to homogeneous simplex or tensor meshes.
8846 
8847   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8848 
8849 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8850 @*/
8851 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8852 {
8853   DMPlexInterpolatedFlag interp;
8854   DMPolytopeType         ct;
8855   PetscInt               vStart, vEnd, cStart, cEnd, c;
8856 
8857   PetscFunctionBegin;
8858   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8859   PetscCall(DMPlexIsInterpolated(dm, &interp));
8860   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8861   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8862   for (c = cStart; c < cEnd; ++c) {
8863     PetscInt *closure = NULL;
8864     PetscInt  coneSize, closureSize, cl, Nv = 0;
8865 
8866     PetscCall(DMPlexGetCellType(dm, c, &ct));
8867     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8868     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8869     if (interp == DMPLEX_INTERPOLATED_FULL) {
8870       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8871       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));
8872     }
8873     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8874     for (cl = 0; cl < closureSize * 2; cl += 2) {
8875       const PetscInt p = closure[cl];
8876       if ((p >= vStart) && (p < vEnd)) ++Nv;
8877     }
8878     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8879     /* Special Case: Tensor faces with identified vertices */
8880     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8881       PetscInt unsplit;
8882 
8883       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8884       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8885     }
8886     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));
8887   }
8888   PetscFunctionReturn(PETSC_SUCCESS);
8889 }
8890 
8891 /*@
8892   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8893 
8894   Collective
8895 
8896   Input Parameters:
8897 + dm - The `DMPLEX` object
8898 - cellHeight - Normally 0
8899 
8900   Level: developer
8901 
8902   Notes:
8903   This is a useful diagnostic when creating meshes programmatically.
8904   This routine is only relevant for meshes that are fully interpolated across all ranks.
8905   It will error out if a partially interpolated mesh is given on some rank.
8906   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8907 
8908   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8909 
8910 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8911 @*/
8912 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8913 {
8914   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8915   DMPlexInterpolatedFlag interpEnum;
8916 
8917   PetscFunctionBegin;
8918   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8919   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8920   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8921   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8922     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8923     PetscFunctionReturn(PETSC_SUCCESS);
8924   }
8925 
8926   PetscCall(DMGetDimension(dm, &dim));
8927   PetscCall(DMPlexGetDepth(dm, &depth));
8928   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8929   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8930     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8931     for (c = cStart; c < cEnd; ++c) {
8932       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8933       const DMPolytopeType *faceTypes;
8934       DMPolytopeType        ct;
8935       PetscInt              numFaces, coneSize, f;
8936       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8937 
8938       PetscCall(DMPlexGetCellType(dm, c, &ct));
8939       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8940       if (unsplit) continue;
8941       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8942       PetscCall(DMPlexGetCone(dm, c, &cone));
8943       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8944       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8945       for (cl = 0; cl < closureSize * 2; cl += 2) {
8946         const PetscInt p = closure[cl];
8947         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8948       }
8949       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8950       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);
8951       for (f = 0; f < numFaces; ++f) {
8952         DMPolytopeType fct;
8953         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8954 
8955         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8956         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8957         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8958           const PetscInt p = fclosure[cl];
8959           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8960         }
8961         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]);
8962         for (v = 0; v < fnumCorners; ++v) {
8963           if (fclosure[v] != faces[fOff + v]) {
8964             PetscInt v1;
8965 
8966             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8967             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8968             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8969             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8970             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8971             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]);
8972           }
8973         }
8974         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8975         fOff += faceSizes[f];
8976       }
8977       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8978       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8979     }
8980   }
8981   PetscFunctionReturn(PETSC_SUCCESS);
8982 }
8983 
8984 /*@
8985   DMPlexCheckGeometry - Check the geometry of mesh cells
8986 
8987   Input Parameter:
8988 . dm - The `DMPLEX` object
8989 
8990   Level: developer
8991 
8992   Notes:
8993   This is a useful diagnostic when creating meshes programmatically.
8994 
8995   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8996 
8997 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8998 @*/
8999 PetscErrorCode DMPlexCheckGeometry(DM dm)
9000 {
9001   Vec       coordinates;
9002   PetscReal detJ, J[9], refVol = 1.0;
9003   PetscReal vol;
9004   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9005 
9006   PetscFunctionBegin;
9007   PetscCall(DMGetDimension(dm, &dim));
9008   PetscCall(DMGetCoordinateDim(dm, &dE));
9009   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9010   PetscCall(DMPlexGetDepth(dm, &depth));
9011   for (d = 0; d < dim; ++d) refVol *= 2.0;
9012   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9013   /* Make sure local coordinates are created, because that step is collective */
9014   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9015   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9016   for (c = cStart; c < cEnd; ++c) {
9017     DMPolytopeType ct;
9018     PetscInt       unsplit;
9019     PetscBool      ignoreZeroVol = PETSC_FALSE;
9020 
9021     PetscCall(DMPlexGetCellType(dm, c, &ct));
9022     switch (ct) {
9023     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9024     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9025     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9026       ignoreZeroVol = PETSC_TRUE;
9027       break;
9028     default:
9029       break;
9030     }
9031     switch (ct) {
9032     case DM_POLYTOPE_TRI_PRISM:
9033     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9034     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9035     case DM_POLYTOPE_PYRAMID:
9036       continue;
9037     default:
9038       break;
9039     }
9040     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9041     if (unsplit) continue;
9042     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9043     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);
9044     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9045     /* This should work with periodicity since DG coordinates should be used */
9046     if (depth > 1) {
9047       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9048       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);
9049       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9050     }
9051   }
9052   PetscFunctionReturn(PETSC_SUCCESS);
9053 }
9054 
9055 /*@
9056   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9057 
9058   Collective
9059 
9060   Input Parameters:
9061 + dm - The `DMPLEX` object
9062 . pointSF - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9063 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9064 
9065   Level: developer
9066 
9067   Notes:
9068   This is mainly intended for debugging/testing purposes.
9069 
9070   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9071 
9072   Extra roots can come from priodic cuts, where additional points appear on the boundary
9073 
9074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9075 @*/
9076 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9077 {
9078   PetscInt           l, nleaves, nroots, overlap;
9079   const PetscInt    *locals;
9080   const PetscSFNode *remotes;
9081   PetscBool          distributed;
9082   MPI_Comm           comm;
9083   PetscMPIInt        rank;
9084 
9085   PetscFunctionBegin;
9086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9087   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9088   else pointSF = dm->sf;
9089   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9090   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9091   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9092   {
9093     PetscMPIInt mpiFlag;
9094 
9095     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9096     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9097   }
9098   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9099   PetscCall(DMPlexIsDistributed(dm, &distributed));
9100   if (!distributed) {
9101     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);
9102     PetscFunctionReturn(PETSC_SUCCESS);
9103   }
9104   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);
9105   PetscCall(DMPlexGetOverlap(dm, &overlap));
9106 
9107   /* Check SF graph is compatible with DMPlex chart */
9108   {
9109     PetscInt pStart, pEnd, maxLeaf;
9110 
9111     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9112     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9113     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9114     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9115   }
9116 
9117   /* Check Point SF has no local points referenced */
9118   for (l = 0; l < nleaves; l++) {
9119     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);
9120   }
9121 
9122   /* Check there are no cells in interface */
9123   if (!overlap) {
9124     PetscInt cellHeight, cStart, cEnd;
9125 
9126     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9127     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9128     for (l = 0; l < nleaves; ++l) {
9129       const PetscInt point = locals ? locals[l] : l;
9130 
9131       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9132     }
9133   }
9134 
9135   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9136   {
9137     const PetscInt *rootdegree;
9138 
9139     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9140     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9141     for (l = 0; l < nleaves; ++l) {
9142       const PetscInt  point = locals ? locals[l] : l;
9143       const PetscInt *cone;
9144       PetscInt        coneSize, c, idx;
9145 
9146       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9147       PetscCall(DMPlexGetCone(dm, point, &cone));
9148       for (c = 0; c < coneSize; ++c) {
9149         if (!rootdegree[cone[c]]) {
9150           if (locals) {
9151             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9152           } else {
9153             idx = (cone[c] < nleaves) ? cone[c] : -1;
9154           }
9155           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9156         }
9157       }
9158     }
9159   }
9160   PetscFunctionReturn(PETSC_SUCCESS);
9161 }
9162 
9163 /*@
9164   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9165 
9166   Input Parameter:
9167 . dm - The `DMPLEX` object
9168 
9169   Level: developer
9170 
9171   Notes:
9172   This is a useful diagnostic when creating meshes programmatically.
9173 
9174   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9175 
9176   Currently does not include `DMPlexCheckCellShape()`.
9177 
9178 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9179 @*/
9180 PetscErrorCode DMPlexCheck(DM dm)
9181 {
9182   PetscInt cellHeight;
9183 
9184   PetscFunctionBegin;
9185   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9186   PetscCall(DMPlexCheckSymmetry(dm));
9187   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9188   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9189   PetscCall(DMPlexCheckGeometry(dm));
9190   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9191   PetscCall(DMPlexCheckInterfaceCones(dm));
9192   PetscFunctionReturn(PETSC_SUCCESS);
9193 }
9194 
9195 typedef struct cell_stats {
9196   PetscReal min, max, sum, squaresum;
9197   PetscInt  count;
9198 } cell_stats_t;
9199 
9200 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9201 {
9202   PetscInt i, N = *len;
9203 
9204   for (i = 0; i < N; i++) {
9205     cell_stats_t *A = (cell_stats_t *)a;
9206     cell_stats_t *B = (cell_stats_t *)b;
9207 
9208     B->min = PetscMin(A->min, B->min);
9209     B->max = PetscMax(A->max, B->max);
9210     B->sum += A->sum;
9211     B->squaresum += A->squaresum;
9212     B->count += A->count;
9213   }
9214 }
9215 
9216 /*@
9217   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9218 
9219   Collective
9220 
9221   Input Parameters:
9222 + dm        - The `DMPLEX` object
9223 . output    - If true, statistics will be displayed on `stdout`
9224 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9225 
9226   Level: developer
9227 
9228   Notes:
9229   This is mainly intended for debugging/testing purposes.
9230 
9231   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9232 
9233 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9234 @*/
9235 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9236 {
9237   DM           dmCoarse;
9238   cell_stats_t stats, globalStats;
9239   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9240   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9241   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9242   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9243   PetscMPIInt  rank, size;
9244 
9245   PetscFunctionBegin;
9246   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9247   stats.min = PETSC_MAX_REAL;
9248   stats.max = PETSC_MIN_REAL;
9249   stats.sum = stats.squaresum = 0.;
9250   stats.count                 = 0;
9251 
9252   PetscCallMPI(MPI_Comm_size(comm, &size));
9253   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9254   PetscCall(DMGetCoordinateDim(dm, &cdim));
9255   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9256   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9257   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9258   for (c = cStart; c < cEnd; c++) {
9259     PetscInt  i;
9260     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9261 
9262     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9263     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9264     for (i = 0; i < PetscSqr(cdim); ++i) {
9265       frobJ += J[i] * J[i];
9266       frobInvJ += invJ[i] * invJ[i];
9267     }
9268     cond2 = frobJ * frobInvJ;
9269     cond  = PetscSqrtReal(cond2);
9270 
9271     stats.min = PetscMin(stats.min, cond);
9272     stats.max = PetscMax(stats.max, cond);
9273     stats.sum += cond;
9274     stats.squaresum += cond2;
9275     stats.count++;
9276     if (output && cond > limit) {
9277       PetscSection coordSection;
9278       Vec          coordsLocal;
9279       PetscScalar *coords = NULL;
9280       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9281 
9282       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9283       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9284       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9285       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9286       for (i = 0; i < Nv / cdim; ++i) {
9287         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9288         for (d = 0; d < cdim; ++d) {
9289           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9290           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9291         }
9292         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9293       }
9294       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9295       for (cl = 0; cl < clSize * 2; cl += 2) {
9296         const PetscInt edge = closure[cl];
9297 
9298         if ((edge >= eStart) && (edge < eEnd)) {
9299           PetscReal len;
9300 
9301           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9302           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9303         }
9304       }
9305       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9306       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9307     }
9308   }
9309   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9310 
9311   if (size > 1) {
9312     PetscMPIInt  blockLengths[2] = {4, 1};
9313     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9314     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9315     MPI_Op       statReduce;
9316 
9317     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9318     PetscCallMPI(MPI_Type_commit(&statType));
9319     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9320     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9321     PetscCallMPI(MPI_Op_free(&statReduce));
9322     PetscCallMPI(MPI_Type_free(&statType));
9323   } else {
9324     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9325   }
9326   if (rank == 0) {
9327     count = globalStats.count;
9328     min   = globalStats.min;
9329     max   = globalStats.max;
9330     mean  = globalStats.sum / globalStats.count;
9331     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9332   }
9333 
9334   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));
9335   PetscCall(PetscFree2(J, invJ));
9336 
9337   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9338   if (dmCoarse) {
9339     PetscBool isplex;
9340 
9341     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9342     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9343   }
9344   PetscFunctionReturn(PETSC_SUCCESS);
9345 }
9346 
9347 /*@
9348   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9349   orthogonal quality below given tolerance.
9350 
9351   Collective
9352 
9353   Input Parameters:
9354 + dm   - The `DMPLEX` object
9355 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9356 - atol - [0, 1] Absolute tolerance for tagging cells.
9357 
9358   Output Parameters:
9359 + OrthQual      - `Vec` containing orthogonal quality per cell
9360 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9361 
9362   Options Database Keys:
9363 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9364 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9365 
9366   Level: intermediate
9367 
9368   Notes:
9369   Orthogonal quality is given by the following formula:
9370 
9371   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9372 
9373   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
9374   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9375   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9376   calculating the cosine of the angle between these vectors.
9377 
9378   Orthogonal quality ranges from 1 (best) to 0 (worst).
9379 
9380   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9381   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9382 
9383   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9384 
9385 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9386 @*/
9387 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9388 {
9389   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9390   PetscInt              *idx;
9391   PetscScalar           *oqVals;
9392   const PetscScalar     *cellGeomArr, *faceGeomArr;
9393   PetscReal             *ci, *fi, *Ai;
9394   MPI_Comm               comm;
9395   Vec                    cellgeom, facegeom;
9396   DM                     dmFace, dmCell;
9397   IS                     glob;
9398   ISLocalToGlobalMapping ltog;
9399   PetscViewer            vwr;
9400 
9401   PetscFunctionBegin;
9402   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9403   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9404   PetscValidPointer(OrthQual, 4);
9405   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9406   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9407   PetscCall(DMGetDimension(dm, &nc));
9408   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9409   {
9410     DMPlexInterpolatedFlag interpFlag;
9411 
9412     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9413     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9414       PetscMPIInt rank;
9415 
9416       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9417       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9418     }
9419   }
9420   if (OrthQualLabel) {
9421     PetscValidPointer(OrthQualLabel, 5);
9422     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9423     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9424   } else {
9425     *OrthQualLabel = NULL;
9426   }
9427   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9428   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9429   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9430   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9431   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9432   PetscCall(VecCreate(comm, OrthQual));
9433   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9434   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9435   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9436   PetscCall(VecSetUp(*OrthQual));
9437   PetscCall(ISDestroy(&glob));
9438   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9439   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9440   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9441   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9442   PetscCall(VecGetDM(cellgeom, &dmCell));
9443   PetscCall(VecGetDM(facegeom, &dmFace));
9444   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9445   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9446     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9447     PetscInt         cellarr[2], *adj = NULL;
9448     PetscScalar     *cArr, *fArr;
9449     PetscReal        minvalc = 1.0, minvalf = 1.0;
9450     PetscFVCellGeom *cg;
9451 
9452     idx[cellIter] = cell - cStart;
9453     cellarr[0]    = cell;
9454     /* Make indexing into cellGeom easier */
9455     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9456     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9457     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9458     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9459     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9460       PetscInt         i;
9461       const PetscInt   neigh  = adj[cellneigh];
9462       PetscReal        normci = 0, normfi = 0, normai = 0;
9463       PetscFVCellGeom *cgneigh;
9464       PetscFVFaceGeom *fg;
9465 
9466       /* Don't count ourselves in the neighbor list */
9467       if (neigh == cell) continue;
9468       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9469       cellarr[1] = neigh;
9470       {
9471         PetscInt        numcovpts;
9472         const PetscInt *covpts;
9473 
9474         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9475         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9476         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9477       }
9478 
9479       /* Compute c_i, f_i and their norms */
9480       for (i = 0; i < nc; i++) {
9481         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9482         fi[i] = fg->centroid[i] - cg->centroid[i];
9483         Ai[i] = fg->normal[i];
9484         normci += PetscPowReal(ci[i], 2);
9485         normfi += PetscPowReal(fi[i], 2);
9486         normai += PetscPowReal(Ai[i], 2);
9487       }
9488       normci = PetscSqrtReal(normci);
9489       normfi = PetscSqrtReal(normfi);
9490       normai = PetscSqrtReal(normai);
9491 
9492       /* Normalize and compute for each face-cell-normal pair */
9493       for (i = 0; i < nc; i++) {
9494         ci[i] = ci[i] / normci;
9495         fi[i] = fi[i] / normfi;
9496         Ai[i] = Ai[i] / normai;
9497         /* PetscAbs because I don't know if normals are guaranteed to point out */
9498         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9499         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9500       }
9501       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9502       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9503     }
9504     PetscCall(PetscFree(adj));
9505     PetscCall(PetscFree2(cArr, fArr));
9506     /* Defer to cell if they're equal */
9507     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9508     if (OrthQualLabel) {
9509       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9510     }
9511   }
9512   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9513   PetscCall(VecAssemblyBegin(*OrthQual));
9514   PetscCall(VecAssemblyEnd(*OrthQual));
9515   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9516   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9517   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9518   if (OrthQualLabel) {
9519     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9520   }
9521   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9522   PetscCall(PetscViewerDestroy(&vwr));
9523   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9524   PetscFunctionReturn(PETSC_SUCCESS);
9525 }
9526 
9527 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9528  * interpolator construction */
9529 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9530 {
9531   PetscSection section, newSection, gsection;
9532   PetscSF      sf;
9533   PetscBool    hasConstraints, ghasConstraints;
9534 
9535   PetscFunctionBegin;
9536   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9537   PetscValidPointer(odm, 2);
9538   PetscCall(DMGetLocalSection(dm, &section));
9539   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9540   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9541   if (!ghasConstraints) {
9542     PetscCall(PetscObjectReference((PetscObject)dm));
9543     *odm = dm;
9544     PetscFunctionReturn(PETSC_SUCCESS);
9545   }
9546   PetscCall(DMClone(dm, odm));
9547   PetscCall(DMCopyFields(dm, *odm));
9548   PetscCall(DMGetLocalSection(*odm, &newSection));
9549   PetscCall(DMGetPointSF(*odm, &sf));
9550   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9551   PetscCall(DMSetGlobalSection(*odm, gsection));
9552   PetscCall(PetscSectionDestroy(&gsection));
9553   PetscFunctionReturn(PETSC_SUCCESS);
9554 }
9555 
9556 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9557 {
9558   DM        dmco, dmfo;
9559   Mat       interpo;
9560   Vec       rscale;
9561   Vec       cglobalo, clocal;
9562   Vec       fglobal, fglobalo, flocal;
9563   PetscBool regular;
9564 
9565   PetscFunctionBegin;
9566   PetscCall(DMGetFullDM(dmc, &dmco));
9567   PetscCall(DMGetFullDM(dmf, &dmfo));
9568   PetscCall(DMSetCoarseDM(dmfo, dmco));
9569   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9570   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9571   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9572   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9573   PetscCall(DMCreateLocalVector(dmc, &clocal));
9574   PetscCall(VecSet(cglobalo, 0.));
9575   PetscCall(VecSet(clocal, 0.));
9576   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9577   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9578   PetscCall(DMCreateLocalVector(dmf, &flocal));
9579   PetscCall(VecSet(fglobal, 0.));
9580   PetscCall(VecSet(fglobalo, 0.));
9581   PetscCall(VecSet(flocal, 0.));
9582   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9583   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9584   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9585   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9586   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9587   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9588   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9589   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9590   *shift = fglobal;
9591   PetscCall(VecDestroy(&flocal));
9592   PetscCall(VecDestroy(&fglobalo));
9593   PetscCall(VecDestroy(&clocal));
9594   PetscCall(VecDestroy(&cglobalo));
9595   PetscCall(VecDestroy(&rscale));
9596   PetscCall(MatDestroy(&interpo));
9597   PetscCall(DMDestroy(&dmfo));
9598   PetscCall(DMDestroy(&dmco));
9599   PetscFunctionReturn(PETSC_SUCCESS);
9600 }
9601 
9602 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9603 {
9604   PetscObject shifto;
9605   Vec         shift;
9606 
9607   PetscFunctionBegin;
9608   if (!interp) {
9609     Vec rscale;
9610 
9611     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9612     PetscCall(VecDestroy(&rscale));
9613   } else {
9614     PetscCall(PetscObjectReference((PetscObject)interp));
9615   }
9616   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9617   if (!shifto) {
9618     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9619     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9620     shifto = (PetscObject)shift;
9621     PetscCall(VecDestroy(&shift));
9622   }
9623   shift = (Vec)shifto;
9624   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9625   PetscCall(VecAXPY(fineSol, 1.0, shift));
9626   PetscCall(MatDestroy(&interp));
9627   PetscFunctionReturn(PETSC_SUCCESS);
9628 }
9629 
9630 /* Pointwise interpolation
9631      Just code FEM for now
9632      u^f = I u^c
9633      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9634      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9635      I_{ij} = psi^f_i phi^c_j
9636 */
9637 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9638 {
9639   PetscSection gsc, gsf;
9640   PetscInt     m, n;
9641   void        *ctx;
9642   DM           cdm;
9643   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9644 
9645   PetscFunctionBegin;
9646   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9647   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9648   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9649   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9650 
9651   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9652   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9653   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9654   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9655   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9656 
9657   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9658   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9659   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9660   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9661   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9662   if (scaling) {
9663     /* Use naive scaling */
9664     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9665   }
9666   PetscFunctionReturn(PETSC_SUCCESS);
9667 }
9668 
9669 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9670 {
9671   VecScatter ctx;
9672 
9673   PetscFunctionBegin;
9674   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9675   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9676   PetscCall(VecScatterDestroy(&ctx));
9677   PetscFunctionReturn(PETSC_SUCCESS);
9678 }
9679 
9680 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[])
9681 {
9682   const PetscInt Nc = uOff[1] - uOff[0];
9683   PetscInt       c;
9684   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9685 }
9686 
9687 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9688 {
9689   DM           dmc;
9690   PetscDS      ds;
9691   Vec          ones, locmass;
9692   IS           cellIS;
9693   PetscFormKey key;
9694   PetscInt     depth;
9695 
9696   PetscFunctionBegin;
9697   PetscCall(DMClone(dm, &dmc));
9698   PetscCall(DMCopyDisc(dm, dmc));
9699   PetscCall(DMGetDS(dmc, &ds));
9700   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9701   PetscCall(DMCreateGlobalVector(dmc, mass));
9702   PetscCall(DMGetLocalVector(dmc, &ones));
9703   PetscCall(DMGetLocalVector(dmc, &locmass));
9704   PetscCall(DMPlexGetDepth(dmc, &depth));
9705   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9706   PetscCall(VecSet(locmass, 0.0));
9707   PetscCall(VecSet(ones, 1.0));
9708   key.label = NULL;
9709   key.value = 0;
9710   key.field = 0;
9711   key.part  = 0;
9712   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9713   PetscCall(ISDestroy(&cellIS));
9714   PetscCall(VecSet(*mass, 0.0));
9715   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9716   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9717   PetscCall(DMRestoreLocalVector(dmc, &ones));
9718   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9719   PetscCall(DMDestroy(&dmc));
9720   PetscFunctionReturn(PETSC_SUCCESS);
9721 }
9722 
9723 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9724 {
9725   PetscSection gsc, gsf;
9726   PetscInt     m, n;
9727   void        *ctx;
9728   DM           cdm;
9729   PetscBool    regular;
9730 
9731   PetscFunctionBegin;
9732   if (dmFine == dmCoarse) {
9733     DM            dmc;
9734     PetscDS       ds;
9735     PetscWeakForm wf;
9736     Vec           u;
9737     IS            cellIS;
9738     PetscFormKey  key;
9739     PetscInt      depth;
9740 
9741     PetscCall(DMClone(dmFine, &dmc));
9742     PetscCall(DMCopyDisc(dmFine, dmc));
9743     PetscCall(DMGetDS(dmc, &ds));
9744     PetscCall(PetscDSGetWeakForm(ds, &wf));
9745     PetscCall(PetscWeakFormClear(wf));
9746     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9747     PetscCall(DMCreateMatrix(dmc, mass));
9748     PetscCall(DMGetLocalVector(dmc, &u));
9749     PetscCall(DMPlexGetDepth(dmc, &depth));
9750     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9751     PetscCall(MatZeroEntries(*mass));
9752     key.label = NULL;
9753     key.value = 0;
9754     key.field = 0;
9755     key.part  = 0;
9756     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9757     PetscCall(ISDestroy(&cellIS));
9758     PetscCall(DMRestoreLocalVector(dmc, &u));
9759     PetscCall(DMDestroy(&dmc));
9760   } else {
9761     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9762     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9763     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9764     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9765 
9766     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9767     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9768     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9769     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9770 
9771     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9772     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9773     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9774     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9775   }
9776   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9777   PetscFunctionReturn(PETSC_SUCCESS);
9778 }
9779 
9780 /*@
9781   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9782 
9783   Input Parameter:
9784 . dm - The `DMPLEX` object
9785 
9786   Output Parameter:
9787 . regular - The flag
9788 
9789   Level: intermediate
9790 
9791 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9792 @*/
9793 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9794 {
9795   PetscFunctionBegin;
9796   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9797   PetscValidBoolPointer(regular, 2);
9798   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9799   PetscFunctionReturn(PETSC_SUCCESS);
9800 }
9801 
9802 /*@
9803   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9804 
9805   Input Parameters:
9806 + dm - The `DMPLEX` object
9807 - regular - The flag
9808 
9809   Level: intermediate
9810 
9811 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9812 @*/
9813 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9814 {
9815   PetscFunctionBegin;
9816   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9817   ((DM_Plex *)dm->data)->regularRefinement = regular;
9818   PetscFunctionReturn(PETSC_SUCCESS);
9819 }
9820 
9821 /*@
9822   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9823   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9824 
9825   Not Collective
9826 
9827   Input Parameter:
9828 . dm - The `DMPLEX` object
9829 
9830   Output Parameters:
9831 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9832 - anchorIS - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9833 
9834   Level: intermediate
9835 
9836 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9837 @*/
9838 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9839 {
9840   DM_Plex *plex = (DM_Plex *)dm->data;
9841 
9842   PetscFunctionBegin;
9843   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9844   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9845   if (anchorSection) *anchorSection = plex->anchorSection;
9846   if (anchorIS) *anchorIS = plex->anchorIS;
9847   PetscFunctionReturn(PETSC_SUCCESS);
9848 }
9849 
9850 /*@
9851   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9852   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9853   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9854 
9855   Collective
9856 
9857   Input Parameters:
9858 + dm - The `DMPLEX` object
9859 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9860                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9861 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9862 
9863   Level: intermediate
9864 
9865   Notes:
9866   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9867   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9868 
9869   The reference counts of `anchorSection` and `anchorIS` are incremented.
9870 
9871 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9872 @*/
9873 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9874 {
9875   DM_Plex    *plex = (DM_Plex *)dm->data;
9876   PetscMPIInt result;
9877 
9878   PetscFunctionBegin;
9879   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9880   if (anchorSection) {
9881     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9882     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9883     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9884   }
9885   if (anchorIS) {
9886     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9887     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9888     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9889   }
9890 
9891   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9892   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9893   plex->anchorSection = anchorSection;
9894 
9895   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9896   PetscCall(ISDestroy(&plex->anchorIS));
9897   plex->anchorIS = anchorIS;
9898 
9899   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9900     PetscInt        size, a, pStart, pEnd;
9901     const PetscInt *anchors;
9902 
9903     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9904     PetscCall(ISGetLocalSize(anchorIS, &size));
9905     PetscCall(ISGetIndices(anchorIS, &anchors));
9906     for (a = 0; a < size; a++) {
9907       PetscInt p;
9908 
9909       p = anchors[a];
9910       if (p >= pStart && p < pEnd) {
9911         PetscInt dof;
9912 
9913         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9914         if (dof) {
9915           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9916           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9917         }
9918       }
9919     }
9920     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9921   }
9922   /* reset the generic constraints */
9923   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9924   PetscFunctionReturn(PETSC_SUCCESS);
9925 }
9926 
9927 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9928 {
9929   PetscSection anchorSection;
9930   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9931 
9932   PetscFunctionBegin;
9933   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9934   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9935   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9936   PetscCall(PetscSectionGetNumFields(section, &numFields));
9937   if (numFields) {
9938     PetscInt f;
9939     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9940 
9941     for (f = 0; f < numFields; f++) {
9942       PetscInt numComp;
9943 
9944       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9945       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9946     }
9947   }
9948   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9949   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9950   pStart = PetscMax(pStart, sStart);
9951   pEnd   = PetscMin(pEnd, sEnd);
9952   pEnd   = PetscMax(pStart, pEnd);
9953   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9954   for (p = pStart; p < pEnd; p++) {
9955     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9956     if (dof) {
9957       PetscCall(PetscSectionGetDof(section, p, &dof));
9958       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9959       for (f = 0; f < numFields; f++) {
9960         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9961         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9962       }
9963     }
9964   }
9965   PetscCall(PetscSectionSetUp(*cSec));
9966   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9967   PetscFunctionReturn(PETSC_SUCCESS);
9968 }
9969 
9970 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9971 {
9972   PetscSection    aSec;
9973   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9974   const PetscInt *anchors;
9975   PetscInt        numFields, f;
9976   IS              aIS;
9977   MatType         mtype;
9978   PetscBool       iscuda, iskokkos;
9979 
9980   PetscFunctionBegin;
9981   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9982   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9983   PetscCall(PetscSectionGetStorageSize(section, &n));
9984   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9985   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9986   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9987   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9988   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9989   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9990   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9991   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9992   else mtype = MATSEQAIJ;
9993   PetscCall(MatSetType(*cMat, mtype));
9994   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9995   PetscCall(ISGetIndices(aIS, &anchors));
9996   /* cSec will be a subset of aSec and section */
9997   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9998   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9999   PetscCall(PetscMalloc1(m + 1, &i));
10000   i[0] = 0;
10001   PetscCall(PetscSectionGetNumFields(section, &numFields));
10002   for (p = pStart; p < pEnd; p++) {
10003     PetscInt rDof, rOff, r;
10004 
10005     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10006     if (!rDof) continue;
10007     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10008     if (numFields) {
10009       for (f = 0; f < numFields; f++) {
10010         annz = 0;
10011         for (r = 0; r < rDof; r++) {
10012           a = anchors[rOff + r];
10013           if (a < sStart || a >= sEnd) continue;
10014           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10015           annz += aDof;
10016         }
10017         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10018         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10019         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10020       }
10021     } else {
10022       annz = 0;
10023       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10024       for (q = 0; q < dof; q++) {
10025         a = anchors[rOff + q];
10026         if (a < sStart || a >= sEnd) continue;
10027         PetscCall(PetscSectionGetDof(section, a, &aDof));
10028         annz += aDof;
10029       }
10030       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10031       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10032       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10033     }
10034   }
10035   nnz = i[m];
10036   PetscCall(PetscMalloc1(nnz, &j));
10037   offset = 0;
10038   for (p = pStart; p < pEnd; p++) {
10039     if (numFields) {
10040       for (f = 0; f < numFields; f++) {
10041         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10042         for (q = 0; q < dof; q++) {
10043           PetscInt rDof, rOff, r;
10044           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10045           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10046           for (r = 0; r < rDof; r++) {
10047             PetscInt s;
10048 
10049             a = anchors[rOff + r];
10050             if (a < sStart || a >= sEnd) continue;
10051             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10052             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10053             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10054           }
10055         }
10056       }
10057     } else {
10058       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10059       for (q = 0; q < dof; q++) {
10060         PetscInt rDof, rOff, r;
10061         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10062         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10063         for (r = 0; r < rDof; r++) {
10064           PetscInt s;
10065 
10066           a = anchors[rOff + r];
10067           if (a < sStart || a >= sEnd) continue;
10068           PetscCall(PetscSectionGetDof(section, a, &aDof));
10069           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10070           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10071         }
10072       }
10073     }
10074   }
10075   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10076   PetscCall(PetscFree(i));
10077   PetscCall(PetscFree(j));
10078   PetscCall(ISRestoreIndices(aIS, &anchors));
10079   PetscFunctionReturn(PETSC_SUCCESS);
10080 }
10081 
10082 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10083 {
10084   DM_Plex     *plex = (DM_Plex *)dm->data;
10085   PetscSection anchorSection, section, cSec;
10086   Mat          cMat;
10087 
10088   PetscFunctionBegin;
10089   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10090   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10091   if (anchorSection) {
10092     PetscInt Nf;
10093 
10094     PetscCall(DMGetLocalSection(dm, &section));
10095     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10096     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10097     PetscCall(DMGetNumFields(dm, &Nf));
10098     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10099     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10100     PetscCall(PetscSectionDestroy(&cSec));
10101     PetscCall(MatDestroy(&cMat));
10102   }
10103   PetscFunctionReturn(PETSC_SUCCESS);
10104 }
10105 
10106 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10107 {
10108   IS           subis;
10109   PetscSection section, subsection;
10110 
10111   PetscFunctionBegin;
10112   PetscCall(DMGetLocalSection(dm, &section));
10113   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10114   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10115   /* Create subdomain */
10116   PetscCall(DMPlexFilter(dm, label, value, subdm));
10117   /* Create submodel */
10118   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10119   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10120   PetscCall(DMSetLocalSection(*subdm, subsection));
10121   PetscCall(PetscSectionDestroy(&subsection));
10122   PetscCall(DMCopyDisc(dm, *subdm));
10123   /* Create map from submodel to global model */
10124   if (is) {
10125     PetscSection    sectionGlobal, subsectionGlobal;
10126     IS              spIS;
10127     const PetscInt *spmap;
10128     PetscInt       *subIndices;
10129     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10130     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10131 
10132     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10133     PetscCall(ISGetIndices(spIS, &spmap));
10134     PetscCall(PetscSectionGetNumFields(section, &Nf));
10135     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10136     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10137     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10138     for (p = pStart; p < pEnd; ++p) {
10139       PetscInt gdof, pSubSize = 0;
10140 
10141       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10142       if (gdof > 0) {
10143         for (f = 0; f < Nf; ++f) {
10144           PetscInt fdof, fcdof;
10145 
10146           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10147           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10148           pSubSize += fdof - fcdof;
10149         }
10150         subSize += pSubSize;
10151         if (pSubSize) {
10152           if (bs < 0) {
10153             bs = pSubSize;
10154           } else if (bs != pSubSize) {
10155             /* Layout does not admit a pointwise block size */
10156             bs = 1;
10157           }
10158         }
10159       }
10160     }
10161     /* Must have same blocksize on all procs (some might have no points) */
10162     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10163     bsLocal[1] = bs;
10164     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10165     if (bsMinMax[0] != bsMinMax[1]) {
10166       bs = 1;
10167     } else {
10168       bs = bsMinMax[0];
10169     }
10170     PetscCall(PetscMalloc1(subSize, &subIndices));
10171     for (p = pStart; p < pEnd; ++p) {
10172       PetscInt gdof, goff;
10173 
10174       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10175       if (gdof > 0) {
10176         const PetscInt point = spmap[p];
10177 
10178         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10179         for (f = 0; f < Nf; ++f) {
10180           PetscInt fdof, fcdof, fc, f2, poff = 0;
10181 
10182           /* Can get rid of this loop by storing field information in the global section */
10183           for (f2 = 0; f2 < f; ++f2) {
10184             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10185             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10186             poff += fdof - fcdof;
10187           }
10188           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10189           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10190           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10191         }
10192       }
10193     }
10194     PetscCall(ISRestoreIndices(spIS, &spmap));
10195     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10196     if (bs > 1) {
10197       /* We need to check that the block size does not come from non-contiguous fields */
10198       PetscInt i, j, set = 1;
10199       for (i = 0; i < subSize; i += bs) {
10200         for (j = 0; j < bs; ++j) {
10201           if (subIndices[i + j] != subIndices[i] + j) {
10202             set = 0;
10203             break;
10204           }
10205         }
10206       }
10207       if (set) PetscCall(ISSetBlockSize(*is, bs));
10208     }
10209     /* Attach nullspace */
10210     for (f = 0; f < Nf; ++f) {
10211       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10212       if ((*subdm)->nullspaceConstructors[f]) break;
10213     }
10214     if (f < Nf) {
10215       MatNullSpace nullSpace;
10216       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10217 
10218       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10219       PetscCall(MatNullSpaceDestroy(&nullSpace));
10220     }
10221   }
10222   PetscFunctionReturn(PETSC_SUCCESS);
10223 }
10224 
10225 /*@
10226   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10227 
10228   Input Parameters:
10229 + dm - The `DM`
10230 - dummy - unused argument
10231 
10232   Options Database Key:
10233 . -dm_plex_monitor_throughput - Activate the monitor
10234 
10235   Level: developer
10236 
10237 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10238 @*/
10239 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10240 {
10241 #if defined(PETSC_USE_LOG)
10242   PetscStageLog      stageLog;
10243   PetscLogEvent      event;
10244   PetscLogStage      stage;
10245   PetscEventPerfInfo eventInfo;
10246   PetscReal          cellRate, flopRate;
10247   PetscInt           cStart, cEnd, Nf, N;
10248   const char        *name;
10249 #endif
10250 
10251   PetscFunctionBegin;
10252   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10253 #if defined(PETSC_USE_LOG)
10254   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10255   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10256   PetscCall(DMGetNumFields(dm, &Nf));
10257   PetscCall(PetscLogGetStageLog(&stageLog));
10258   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10259   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10260   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10261   N        = (cEnd - cStart) * Nf * eventInfo.count;
10262   flopRate = eventInfo.flops / eventInfo.time;
10263   cellRate = N / eventInfo.time;
10264   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)));
10265 #else
10266   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10267 #endif
10268   PetscFunctionReturn(PETSC_SUCCESS);
10269 }
10270