xref: /petsc/src/dm/impls/plex/plex.c (revision e8e188d2d8d8bcc000607a9a2a524e07d1c8a16a)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm - The `DMPLEX` object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(PETSC_SUCCESS);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(PETSC_SUCCESS);
49 }
50 
51 /*@
52   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
53 
54   Input Parameters:
55 + dm     - The `DMPLEX` object
56 - height - The cell height in the Plex, 0 is the default
57 
58   Output Parameters:
59 + cStart - The first "normal" cell
60 - cEnd   - The upper bound on "normal"" cells
61 
62   Level: developer
63 
64   Note:
65   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
66 
67 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(PETSC_SUCCESS);
106 }
107 
108 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
109 {
110   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
111   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
112 
113   PetscFunctionBegin;
114   *ft = PETSC_VTK_INVALID;
115   PetscCall(DMGetCoordinateDim(dm, &cdim));
116   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
118   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
119   if (field >= 0) {
120     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
121     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
122   } else {
123     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
124     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
125   }
126   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(PETSC_SUCCESS);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective
154 
155   Input Parameters:
156 + dm     - The `DMPLEX` object
157 . n      - The number of vectors
158 . u      - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
202         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
203         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(PETSC_SUCCESS);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *array;
253   PetscReal          lbound[3], ubound[3];
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
278   PetscCall(PetscDrawClear(draw));
279 
280   /* Could implement something like DMDASelectFields() */
281   for (f = 0; f < Nf; ++f) {
282     DM          fdm = dm;
283     Vec         fv  = v;
284     IS          fis;
285     char        prefix[PETSC_MAX_PATH_LEN];
286     const char *fname;
287 
288     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
289     PetscCall(PetscSectionGetFieldName(s, f, &fname));
290 
291     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
292     else prefix[0] = '\0';
293     if (Nf > 1) {
294       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
295       PetscCall(VecGetSubVector(v, fis, &fv));
296       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
297       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
298     }
299     for (comp = 0; comp < Nc; ++comp, ++w) {
300       PetscInt nmax = 2;
301 
302       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
303       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
304       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
305       PetscCall(PetscDrawSetTitle(draw, title));
306 
307       /* TODO Get max and min only for this component */
308       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
309       if (!flg) {
310         PetscCall(VecMin(fv, NULL, &vbound[0]));
311         PetscCall(VecMax(fv, NULL, &vbound[1]));
312         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
313       }
314 
315       PetscCall(PetscDrawGetPopup(draw, &popup));
316       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
317       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
318       PetscCall(VecGetArrayRead(fv, &array));
319       for (c = cStart; c < cEnd; ++c) {
320         PetscScalar       *coords = NULL, *a = NULL;
321         const PetscScalar *coords_arr;
322         PetscBool          isDG;
323         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
324 
325         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
326         if (a) {
327           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
328           color[1] = color[2] = color[3] = color[0];
329         } else {
330           PetscScalar *vals = NULL;
331           PetscInt     numVals, va;
332 
333           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
334           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
335           switch (numVals / Nc) {
336           case 3: /* P1 Triangle */
337           case 4: /* P1 Quadrangle */
338             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
339             break;
340           case 6: /* P2 Triangle */
341           case 8: /* P2 Quadrangle */
342             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
343             break;
344           default:
345             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
346           }
347           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
348         }
349         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
350         switch (numCoords) {
351         case 6:
352         case 12: /* Localized triangle */
353           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
354           break;
355         case 8:
356         case 16: /* Localized quadrilateral */
357           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
358           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
359           break;
360         default:
361           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
362         }
363         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
364       }
365       PetscCall(VecRestoreArrayRead(fv, &array));
366       PetscCall(PetscDrawFlush(draw));
367       PetscCall(PetscDrawPause(draw));
368       PetscCall(PetscDrawSave(draw));
369     }
370     if (Nf > 1) {
371       PetscCall(VecRestoreSubVector(v, fis, &fv));
372       PetscCall(ISDestroy(&fis));
373       PetscCall(DMDestroy(&fdm));
374     }
375   }
376   PetscFunctionReturn(PETSC_SUCCESS);
377 }
378 
379 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
380 {
381   DM        dm;
382   PetscDraw draw;
383   PetscInt  dim;
384   PetscBool isnull;
385 
386   PetscFunctionBegin;
387   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
388   PetscCall(PetscDrawIsNull(draw, &isnull));
389   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
390 
391   PetscCall(VecGetDM(v, &dm));
392   PetscCall(DMGetCoordinateDim(dm, &dim));
393   switch (dim) {
394   case 1:
395     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
396     break;
397   case 2:
398     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
399     break;
400   default:
401     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
402   }
403   PetscFunctionReturn(PETSC_SUCCESS);
404 }
405 
406 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
407 {
408   DM                      dm;
409   Vec                     locv;
410   const char             *name;
411   PetscSection            section;
412   PetscInt                pStart, pEnd;
413   PetscInt                numFields;
414   PetscViewerVTKFieldType ft;
415 
416   PetscFunctionBegin;
417   PetscCall(VecGetDM(v, &dm));
418   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
419   PetscCall(PetscObjectGetName((PetscObject)v, &name));
420   PetscCall(PetscObjectSetName((PetscObject)locv, name));
421   PetscCall(VecCopy(v, locv));
422   PetscCall(DMGetLocalSection(dm, &section));
423   PetscCall(PetscSectionGetNumFields(section, &numFields));
424   if (!numFields) {
425     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
426     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
427   } else {
428     PetscInt f;
429 
430     for (f = 0; f < numFields; f++) {
431       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
432       if (ft == PETSC_VTK_INVALID) continue;
433       PetscCall(PetscObjectReference((PetscObject)locv));
434       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
435     }
436     PetscCall(VecDestroy(&locv));
437   }
438   PetscFunctionReturn(PETSC_SUCCESS);
439 }
440 
441 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
442 {
443   DM        dm;
444   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
445 
446   PetscFunctionBegin;
447   PetscCall(VecGetDM(v, &dm));
448   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
449   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
450   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
453   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
454   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
455     PetscInt    i, numFields;
456     PetscObject fe;
457     PetscBool   fem  = PETSC_FALSE;
458     Vec         locv = v;
459     const char *name;
460     PetscInt    step;
461     PetscReal   time;
462 
463     PetscCall(DMGetNumFields(dm, &numFields));
464     for (i = 0; i < numFields; i++) {
465       PetscCall(DMGetField(dm, i, NULL, &fe));
466       if (fe->classid == PETSCFE_CLASSID) {
467         fem = PETSC_TRUE;
468         break;
469       }
470     }
471     if (fem) {
472       PetscObject isZero;
473 
474       PetscCall(DMGetLocalVector(dm, &locv));
475       PetscCall(PetscObjectGetName((PetscObject)v, &name));
476       PetscCall(PetscObjectSetName((PetscObject)locv, name));
477       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
478       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
479       PetscCall(VecCopy(v, locv));
480       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
481       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
482     }
483     if (isvtk) {
484       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
485     } else if (ishdf5) {
486 #if defined(PETSC_HAVE_HDF5)
487       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
488 #else
489       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
490 #endif
491     } else if (isdraw) {
492       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
493     } else if (isglvis) {
494       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
495       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
496       PetscCall(VecView_GLVis(locv, viewer));
497     } else if (iscgns) {
498 #if defined(PETSC_HAVE_CGNS)
499       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
500 #else
501       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
502 #endif
503     }
504     if (fem) {
505       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
506       PetscCall(DMRestoreLocalVector(dm, &locv));
507     }
508   } else {
509     PetscBool isseq;
510 
511     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
512     if (isseq) PetscCall(VecView_Seq(v, viewer));
513     else PetscCall(VecView_MPI(v, viewer));
514   }
515   PetscFunctionReturn(PETSC_SUCCESS);
516 }
517 
518 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
519 {
520   DM        dm;
521   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
527   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
528   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
529   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
532   if (isvtk || isdraw || isglvis || iscgns) {
533     Vec         locv;
534     PetscObject isZero;
535     const char *name;
536 
537     PetscCall(DMGetLocalVector(dm, &locv));
538     PetscCall(PetscObjectGetName((PetscObject)v, &name));
539     PetscCall(PetscObjectSetName((PetscObject)locv, name));
540     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
541     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
542     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
543     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
544     PetscCall(VecView_Plex_Local(locv, viewer));
545     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
546     PetscCall(DMRestoreLocalVector(dm, &locv));
547   } else if (ishdf5) {
548 #if defined(PETSC_HAVE_HDF5)
549     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
550 #else
551     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
552 #endif
553   } else if (isexodusii) {
554 #if defined(PETSC_HAVE_EXODUSII)
555     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
556 #else
557     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
558 #endif
559   } else {
560     PetscBool isseq;
561 
562     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
563     if (isseq) PetscCall(VecView_Seq(v, viewer));
564     else PetscCall(VecView_MPI(v, viewer));
565   }
566   PetscFunctionReturn(PETSC_SUCCESS);
567 }
568 
569 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
570 {
571   DM                dm;
572   MPI_Comm          comm;
573   PetscViewerFormat format;
574   Vec               v;
575   PetscBool         isvtk, ishdf5;
576 
577   PetscFunctionBegin;
578   PetscCall(VecGetDM(originalv, &dm));
579   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
580   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
581   PetscCall(PetscViewerGetFormat(viewer, &format));
582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
583   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
584   if (format == PETSC_VIEWER_NATIVE) {
585     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
586     /* this need a better fix */
587     if (dm->useNatural) {
588       if (dm->sfNatural) {
589         const char *vecname;
590         PetscInt    n, nroots;
591 
592         PetscCall(VecGetLocalSize(originalv, &n));
593         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
594         if (n == nroots) {
595           PetscCall(DMPlexCreateNaturalVector(dm, &v));
596           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
597           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
598           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
599           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
600         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
601       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
602     } else v = originalv;
603   } else v = originalv;
604 
605   if (ishdf5) {
606 #if defined(PETSC_HAVE_HDF5)
607     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
608 #else
609     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
610 #endif
611   } else if (isvtk) {
612     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
613   } else {
614     PetscBool isseq;
615 
616     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
617     if (isseq) PetscCall(VecView_Seq(v, viewer));
618     else PetscCall(VecView_MPI(v, viewer));
619   }
620   if (v != originalv) PetscCall(VecDestroy(&v));
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
625 {
626   DM        dm;
627   PetscBool ishdf5;
628 
629   PetscFunctionBegin;
630   PetscCall(VecGetDM(v, &dm));
631   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
633   if (ishdf5) {
634     DM          dmBC;
635     Vec         gv;
636     const char *name;
637 
638     PetscCall(DMGetOutputDM(dm, &dmBC));
639     PetscCall(DMGetGlobalVector(dmBC, &gv));
640     PetscCall(PetscObjectGetName((PetscObject)v, &name));
641     PetscCall(PetscObjectSetName((PetscObject)gv, name));
642     PetscCall(VecLoad_Default(gv, viewer));
643     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
644     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
645     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
646   } else PetscCall(VecLoad_Default(v, viewer));
647   PetscFunctionReturn(PETSC_SUCCESS);
648 }
649 
650 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
651 {
652   DM        dm;
653   PetscBool ishdf5, isexodusii;
654 
655   PetscFunctionBegin;
656   PetscCall(VecGetDM(v, &dm));
657   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
659   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
660   if (ishdf5) {
661 #if defined(PETSC_HAVE_HDF5)
662     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
665 #endif
666   } else if (isexodusii) {
667 #if defined(PETSC_HAVE_EXODUSII)
668     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
669 #else
670     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
671 #endif
672   } else PetscCall(VecLoad_Default(v, viewer));
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   PetscViewerFormat format;
680   PetscBool         ishdf5;
681 
682   PetscFunctionBegin;
683   PetscCall(VecGetDM(originalv, &dm));
684   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
685   PetscCall(PetscViewerGetFormat(viewer, &format));
686   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
687   if (format == PETSC_VIEWER_NATIVE) {
688     if (dm->useNatural) {
689       if (dm->sfNatural) {
690         if (ishdf5) {
691 #if defined(PETSC_HAVE_HDF5)
692           Vec         v;
693           const char *vecname;
694 
695           PetscCall(DMPlexCreateNaturalVector(dm, &v));
696           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
697           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
698           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
699           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
700           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
701           PetscCall(VecDestroy(&v));
702 #else
703           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
704 #endif
705         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
706       }
707     } else PetscCall(VecLoad_Default(originalv, viewer));
708   }
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
713 {
714   PetscSection       coordSection;
715   Vec                coordinates;
716   DMLabel            depthLabel, celltypeLabel;
717   const char        *name[4];
718   const PetscScalar *a;
719   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
720 
721   PetscFunctionBegin;
722   PetscCall(DMGetDimension(dm, &dim));
723   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
724   PetscCall(DMGetCoordinateSection(dm, &coordSection));
725   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
726   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
727   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
728   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
729   PetscCall(VecGetArrayRead(coordinates, &a));
730   name[0]       = "vertex";
731   name[1]       = "edge";
732   name[dim - 1] = "face";
733   name[dim]     = "cell";
734   for (c = cStart; c < cEnd; ++c) {
735     PetscInt *closure = NULL;
736     PetscInt  closureSize, cl, ct;
737 
738     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
739     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
740     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
741     PetscCall(PetscViewerASCIIPushTab(viewer));
742     for (cl = 0; cl < closureSize * 2; cl += 2) {
743       PetscInt point = closure[cl], depth, dof, off, d, p;
744 
745       if ((point < pStart) || (point >= pEnd)) continue;
746       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
747       if (!dof) continue;
748       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
749       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
750       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
751       for (p = 0; p < dof / dim; ++p) {
752         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
753         for (d = 0; d < dim; ++d) {
754           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
755           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
756         }
757         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
758       }
759       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
760     }
761     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
762     PetscCall(PetscViewerASCIIPopTab(viewer));
763   }
764   PetscCall(VecRestoreArrayRead(coordinates, &a));
765   PetscFunctionReturn(PETSC_SUCCESS);
766 }
767 
768 typedef enum {
769   CS_CARTESIAN,
770   CS_POLAR,
771   CS_CYLINDRICAL,
772   CS_SPHERICAL
773 } CoordSystem;
774 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
775 
776 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
777 {
778   PetscInt i;
779 
780   PetscFunctionBegin;
781   if (dim > 3) {
782     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
783   } else {
784     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
785 
786     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
787     switch (cs) {
788     case CS_CARTESIAN:
789       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
790       break;
791     case CS_POLAR:
792       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
793       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
794       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
795       break;
796     case CS_CYLINDRICAL:
797       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       trcoords[2] = coords[2];
801       break;
802     case CS_SPHERICAL:
803       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
804       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
805       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
806       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
807       break;
808     }
809     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
810   }
811   PetscFunctionReturn(PETSC_SUCCESS);
812 }
813 
814 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
815 {
816   DM_Plex          *mesh = (DM_Plex *)dm->data;
817   DM                cdm, cdmCell;
818   PetscSection      coordSection, coordSectionCell;
819   Vec               coordinates, coordinatesCell;
820   PetscViewerFormat format;
821 
822   PetscFunctionBegin;
823   PetscCall(PetscViewerGetFormat(viewer, &format));
824   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
825     const char *name;
826     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
827     PetscInt    pStart, pEnd, p, numLabels, l;
828     PetscMPIInt rank, size;
829 
830     PetscCall(DMGetCoordinateDM(dm, &cdm));
831     PetscCall(DMGetCoordinateSection(dm, &coordSection));
832     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
833     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
834     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
835     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
836     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
837     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
838     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
839     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
840     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
841     PetscCall(DMGetDimension(dm, &dim));
842     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
843     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
844     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
845     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
847     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
848     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
849     for (p = pStart; p < pEnd; ++p) {
850       PetscInt dof, off, s;
851 
852       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
853       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
854       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
855     }
856     PetscCall(PetscViewerFlush(viewer));
857     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
858     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
859     for (p = pStart; p < pEnd; ++p) {
860       PetscInt dof, off, c;
861 
862       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
863       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
864       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
865     }
866     PetscCall(PetscViewerFlush(viewer));
867     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
868     if (coordSection && coordinates) {
869       CoordSystem        cs = CS_CARTESIAN;
870       const PetscScalar *array, *arrayCell = NULL;
871       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
872       PetscMPIInt        rank;
873       const char        *name;
874 
875       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
876       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
877       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
878       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
879       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
880       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
881       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
882       pStart = PetscMin(pvStart, pcStart);
883       pEnd   = PetscMax(pvEnd, pcEnd);
884       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
885       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
886       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
887       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
888 
889       PetscCall(VecGetArrayRead(coordinates, &array));
890       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
891       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
892       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
893       for (p = pStart; p < pEnd; ++p) {
894         PetscInt dof, off;
895 
896         if (p >= pvStart && p < pvEnd) {
897           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
898           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
899           if (dof) {
900             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
901             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
902             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
903           }
904         }
905         if (cdmCell && p >= pcStart && p < pcEnd) {
906           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
907           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
908           if (dof) {
909             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
910             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
911             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
912           }
913         }
914       }
915       PetscCall(PetscViewerFlush(viewer));
916       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
917       PetscCall(VecRestoreArrayRead(coordinates, &array));
918       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
919     }
920     PetscCall(DMGetNumLabels(dm, &numLabels));
921     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
922     for (l = 0; l < numLabels; ++l) {
923       DMLabel     label;
924       PetscBool   isdepth;
925       const char *name;
926 
927       PetscCall(DMGetLabelName(dm, l, &name));
928       PetscCall(PetscStrcmp(name, "depth", &isdepth));
929       if (isdepth) continue;
930       PetscCall(DMGetLabel(dm, name, &label));
931       PetscCall(DMLabelView(label, viewer));
932     }
933     if (size > 1) {
934       PetscSF sf;
935 
936       PetscCall(DMGetPointSF(dm, &sf));
937       PetscCall(PetscSFView(sf, viewer));
938     }
939     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
940     PetscCall(PetscViewerFlush(viewer));
941   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
942     const char  *name, *color;
943     const char  *defcolors[3]  = {"gray", "orange", "green"};
944     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
945     char         lname[PETSC_MAX_PATH_LEN];
946     PetscReal    scale      = 2.0;
947     PetscReal    tikzscale  = 1.0;
948     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
949     double       tcoords[3];
950     PetscScalar *coords;
951     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
952     PetscMPIInt  rank, size;
953     char       **names, **colors, **lcolors;
954     PetscBool    flg, lflg;
955     PetscBT      wp = NULL;
956     PetscInt     pEnd, pStart;
957 
958     PetscCall(DMGetCoordinateDM(dm, &cdm));
959     PetscCall(DMGetCoordinateSection(dm, &coordSection));
960     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
961     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
962     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
963     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
964     PetscCall(DMGetDimension(dm, &dim));
965     PetscCall(DMPlexGetDepth(dm, &depth));
966     PetscCall(DMGetNumLabels(dm, &numLabels));
967     numLabels  = PetscMax(numLabels, 10);
968     numColors  = 10;
969     numLColors = 10;
970     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
971     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
972     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
973     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
974     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
975     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
976     n = 4;
977     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
978     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
979     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
980     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
981     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
982     if (!useLabels) numLabels = 0;
983     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
984     if (!useColors) {
985       numColors = 3;
986       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
987     }
988     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
989     if (!useColors) {
990       numLColors = 4;
991       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
992     }
993     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
994     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
995     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
996     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
997     if (depth < dim) plotEdges = PETSC_FALSE;
998     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
999 
1000     /* filter points with labelvalue != labeldefaultvalue */
1001     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1002     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1003     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1004     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1005     if (lflg) {
1006       DMLabel lbl;
1007 
1008       PetscCall(DMGetLabel(dm, lname, &lbl));
1009       if (lbl) {
1010         PetscInt val, defval;
1011 
1012         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1013         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1014         for (c = pStart; c < pEnd; c++) {
1015           PetscInt *closure = NULL;
1016           PetscInt  closureSize;
1017 
1018           PetscCall(DMLabelGetValue(lbl, c, &val));
1019           if (val == defval) continue;
1020 
1021           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1022           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1023           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1024         }
1025       }
1026     }
1027 
1028     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1029     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1030     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1031     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1032 \\documentclass[tikz]{standalone}\n\n\
1033 \\usepackage{pgflibraryshapes}\n\
1034 \\usetikzlibrary{backgrounds}\n\
1035 \\usetikzlibrary{arrows}\n\
1036 \\begin{document}\n"));
1037     if (size > 1) {
1038       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1039       for (p = 0; p < size; ++p) {
1040         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1041         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1042       }
1043       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1044     }
1045     if (drawHasse) {
1046       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1047 
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1060     }
1061     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1062 
1063     /* Plot vertices */
1064     PetscCall(VecGetArray(coordinates, &coords));
1065     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1066     for (v = vStart; v < vEnd; ++v) {
1067       PetscInt  off, dof, d;
1068       PetscBool isLabeled = PETSC_FALSE;
1069 
1070       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1071       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1072       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1073       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1074       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1075       for (d = 0; d < dof; ++d) {
1076         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1077         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1078       }
1079       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1080       if (dim == 3) {
1081         PetscReal tmp = tcoords[1];
1082         tcoords[1]    = tcoords[2];
1083         tcoords[2]    = -tmp;
1084       }
1085       for (d = 0; d < dof; ++d) {
1086         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1088       }
1089       if (drawHasse) color = colors[0 % numColors];
1090       else color = colors[rank % numColors];
1091       for (l = 0; l < numLabels; ++l) {
1092         PetscInt val;
1093         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1094         if (val >= 0) {
1095           color     = lcolors[l % numLColors];
1096           isLabeled = PETSC_TRUE;
1097           break;
1098         }
1099       }
1100       if (drawNumbers[0]) {
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1102       } else if (drawColors[0]) {
1103         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1104       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1105     }
1106     PetscCall(VecRestoreArray(coordinates, &coords));
1107     PetscCall(PetscViewerFlush(viewer));
1108     /* Plot edges */
1109     if (plotEdges) {
1110       PetscCall(VecGetArray(coordinates, &coords));
1111       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1112       for (e = eStart; e < eEnd; ++e) {
1113         const PetscInt *cone;
1114         PetscInt        coneSize, offA, offB, dof, d;
1115 
1116         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1117         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1118         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1119         PetscCall(DMPlexGetCone(dm, e, &cone));
1120         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1121         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1122         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1123         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1124         for (d = 0; d < dof; ++d) {
1125           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1126           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1127         }
1128         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1129         if (dim == 3) {
1130           PetscReal tmp = tcoords[1];
1131           tcoords[1]    = tcoords[2];
1132           tcoords[2]    = -tmp;
1133         }
1134         for (d = 0; d < dof; ++d) {
1135           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1136           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1137         }
1138         if (drawHasse) color = colors[1 % numColors];
1139         else color = colors[rank % numColors];
1140         for (l = 0; l < numLabels; ++l) {
1141           PetscInt val;
1142           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1143           if (val >= 0) {
1144             color = lcolors[l % numLColors];
1145             break;
1146           }
1147         }
1148         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1149       }
1150       PetscCall(VecRestoreArray(coordinates, &coords));
1151       PetscCall(PetscViewerFlush(viewer));
1152       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1153     }
1154     /* Plot cells */
1155     if (dim == 3 || !drawNumbers[1]) {
1156       for (e = eStart; e < eEnd; ++e) {
1157         const PetscInt *cone;
1158 
1159         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1160         color = colors[rank % numColors];
1161         for (l = 0; l < numLabels; ++l) {
1162           PetscInt val;
1163           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1164           if (val >= 0) {
1165             color = lcolors[l % numLColors];
1166             break;
1167           }
1168         }
1169         PetscCall(DMPlexGetCone(dm, e, &cone));
1170         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1171       }
1172     } else {
1173       DMPolytopeType ct;
1174 
1175       /* Drawing a 2D polygon */
1176       for (c = cStart; c < cEnd; ++c) {
1177         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1178         PetscCall(DMPlexGetCellType(dm, c, &ct));
1179         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1180           const PetscInt *cone;
1181           PetscInt        coneSize, e;
1182 
1183           PetscCall(DMPlexGetCone(dm, c, &cone));
1184           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1185           for (e = 0; e < coneSize; ++e) {
1186             const PetscInt *econe;
1187 
1188             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1189             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1190           }
1191         } else {
1192           PetscInt *closure = NULL;
1193           PetscInt  closureSize, Nv = 0, v;
1194 
1195           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1196           for (p = 0; p < closureSize * 2; p += 2) {
1197             const PetscInt point = closure[p];
1198 
1199             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1200           }
1201           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1202           for (v = 0; v <= Nv; ++v) {
1203             const PetscInt vertex = closure[v % Nv];
1204 
1205             if (v > 0) {
1206               if (plotEdges) {
1207                 const PetscInt *edge;
1208                 PetscInt        endpoints[2], ne;
1209 
1210                 endpoints[0] = closure[v - 1];
1211                 endpoints[1] = vertex;
1212                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1213                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1214                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1215                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1216               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1217             }
1218             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1219           }
1220           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1221           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1222         }
1223       }
1224     }
1225     for (c = cStart; c < cEnd; ++c) {
1226       double             ccoords[3] = {0.0, 0.0, 0.0};
1227       PetscBool          isLabeled  = PETSC_FALSE;
1228       PetscScalar       *cellCoords = NULL;
1229       const PetscScalar *array;
1230       PetscInt           numCoords, cdim, d;
1231       PetscBool          isDG;
1232 
1233       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1234       PetscCall(DMGetCoordinateDim(dm, &cdim));
1235       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1236       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1237       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1238       for (p = 0; p < numCoords / cdim; ++p) {
1239         for (d = 0; d < cdim; ++d) {
1240           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1241           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1242         }
1243         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1244         if (cdim == 3) {
1245           PetscReal tmp = tcoords[1];
1246           tcoords[1]    = tcoords[2];
1247           tcoords[2]    = -tmp;
1248         }
1249         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1250       }
1251       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1252       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1253       for (d = 0; d < cdim; ++d) {
1254         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1255         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1256       }
1257       if (drawHasse) color = colors[depth % numColors];
1258       else color = colors[rank % numColors];
1259       for (l = 0; l < numLabels; ++l) {
1260         PetscInt val;
1261         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1262         if (val >= 0) {
1263           color     = lcolors[l % numLColors];
1264           isLabeled = PETSC_TRUE;
1265           break;
1266         }
1267       }
1268       if (drawNumbers[dim]) {
1269         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1270       } else if (drawColors[dim]) {
1271         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1272       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1273     }
1274     if (drawHasse) {
1275       color = colors[depth % numColors];
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1277       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1279       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1281 
1282       color = colors[1 % numColors];
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1286       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1288 
1289       color = colors[0 % numColors];
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1295 
1296       for (p = pStart; p < pEnd; ++p) {
1297         const PetscInt *cone;
1298         PetscInt        coneSize, cp;
1299 
1300         PetscCall(DMPlexGetCone(dm, p, &cone));
1301         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1302         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1303       }
1304     }
1305     PetscCall(PetscViewerFlush(viewer));
1306     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1307     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1308     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1309     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1310     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1311     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1312     PetscCall(PetscFree3(names, colors, lcolors));
1313     PetscCall(PetscBTDestroy(&wp));
1314   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1315     Vec                    cown, acown;
1316     VecScatter             sct;
1317     ISLocalToGlobalMapping g2l;
1318     IS                     gid, acis;
1319     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1320     MPI_Group              ggroup, ngroup;
1321     PetscScalar           *array, nid;
1322     const PetscInt        *idxs;
1323     PetscInt              *idxs2, *start, *adjacency, *work;
1324     PetscInt64             lm[3], gm[3];
1325     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1326     PetscMPIInt            d1, d2, rank;
1327 
1328     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1329     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1330 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1331     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1332 #endif
1333     if (ncomm != MPI_COMM_NULL) {
1334       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1335       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1336       d1 = 0;
1337       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1338       nid = d2;
1339       PetscCallMPI(MPI_Group_free(&ggroup));
1340       PetscCallMPI(MPI_Group_free(&ngroup));
1341       PetscCallMPI(MPI_Comm_free(&ncomm));
1342     } else nid = 0.0;
1343 
1344     /* Get connectivity */
1345     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1346     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1347 
1348     /* filter overlapped local cells */
1349     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1350     PetscCall(ISGetIndices(gid, &idxs));
1351     PetscCall(ISGetLocalSize(gid, &cum));
1352     PetscCall(PetscMalloc1(cum, &idxs2));
1353     for (c = cStart, cum = 0; c < cEnd; c++) {
1354       if (idxs[c - cStart] < 0) continue;
1355       idxs2[cum++] = idxs[c - cStart];
1356     }
1357     PetscCall(ISRestoreIndices(gid, &idxs));
1358     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1359     PetscCall(ISDestroy(&gid));
1360     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1361 
1362     /* support for node-aware cell locality */
1363     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1364     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1365     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1366     PetscCall(VecGetArray(cown, &array));
1367     for (c = 0; c < numVertices; c++) array[c] = nid;
1368     PetscCall(VecRestoreArray(cown, &array));
1369     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1370     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1371     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1372     PetscCall(ISDestroy(&acis));
1373     PetscCall(VecScatterDestroy(&sct));
1374     PetscCall(VecDestroy(&cown));
1375 
1376     /* compute edgeCut */
1377     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1378     PetscCall(PetscMalloc1(cum, &work));
1379     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1380     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1381     PetscCall(ISDestroy(&gid));
1382     PetscCall(VecGetArray(acown, &array));
1383     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1384       PetscInt totl;
1385 
1386       totl = start[c + 1] - start[c];
1387       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1388       for (i = 0; i < totl; i++) {
1389         if (work[i] < 0) {
1390           ect += 1;
1391           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1392         }
1393       }
1394     }
1395     PetscCall(PetscFree(work));
1396     PetscCall(VecRestoreArray(acown, &array));
1397     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1398     lm[1] = -numVertices;
1399     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1400     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1401     lm[0] = ect;                     /* edgeCut */
1402     lm[1] = ectn;                    /* node-aware edgeCut */
1403     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1404     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1405     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1406 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1407     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1408 #else
1409     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1410 #endif
1411     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1412     PetscCall(PetscFree(start));
1413     PetscCall(PetscFree(adjacency));
1414     PetscCall(VecDestroy(&acown));
1415   } else {
1416     const char    *name;
1417     PetscInt      *sizes, *hybsizes, *ghostsizes;
1418     PetscInt       locDepth, depth, cellHeight, dim, d;
1419     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1420     PetscInt       numLabels, l, maxSize = 17;
1421     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1422     MPI_Comm       comm;
1423     PetscMPIInt    size, rank;
1424 
1425     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1426     PetscCallMPI(MPI_Comm_size(comm, &size));
1427     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1428     PetscCall(DMGetDimension(dm, &dim));
1429     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1430     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1431     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1432     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1433     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1434     PetscCall(DMPlexGetDepth(dm, &locDepth));
1435     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1436     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1437     gcNum = gcEnd - gcStart;
1438     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1439     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1440     for (d = 0; d <= depth; d++) {
1441       PetscInt Nc[2] = {0, 0}, ict;
1442 
1443       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1444       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1445       ict = ct0;
1446       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1447       ct0 = (DMPolytopeType)ict;
1448       for (p = pStart; p < pEnd; ++p) {
1449         DMPolytopeType ct;
1450 
1451         PetscCall(DMPlexGetCellType(dm, p, &ct));
1452         if (ct == ct0) ++Nc[0];
1453         else ++Nc[1];
1454       }
1455       if (size < maxSize) {
1456         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1457         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1458         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1459         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1460         for (p = 0; p < size; ++p) {
1461           if (rank == 0) {
1462             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1463             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1464             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1465           }
1466         }
1467       } else {
1468         PetscInt locMinMax[2];
1469 
1470         locMinMax[0] = Nc[0] + Nc[1];
1471         locMinMax[1] = Nc[0] + Nc[1];
1472         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1473         locMinMax[0] = Nc[1];
1474         locMinMax[1] = Nc[1];
1475         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1476         if (d == depth) {
1477           locMinMax[0] = gcNum;
1478           locMinMax[1] = gcNum;
1479           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1480         }
1481         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1482         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1483         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1484         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1485       }
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1487     }
1488     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1489     {
1490       const PetscReal *maxCell;
1491       const PetscReal *L;
1492       PetscBool        localized;
1493 
1494       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1495       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1496       if (L || localized) {
1497         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1498         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1499         if (L) {
1500           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1501           for (d = 0; d < dim; ++d) {
1502             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1503             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1504           }
1505           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1506         }
1507         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1508         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1509       }
1510     }
1511     PetscCall(DMGetNumLabels(dm, &numLabels));
1512     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1513     for (l = 0; l < numLabels; ++l) {
1514       DMLabel         label;
1515       const char     *name;
1516       IS              valueIS;
1517       const PetscInt *values;
1518       PetscInt        numValues, v;
1519 
1520       PetscCall(DMGetLabelName(dm, l, &name));
1521       PetscCall(DMGetLabel(dm, name, &label));
1522       PetscCall(DMLabelGetNumValues(label, &numValues));
1523       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1524       PetscCall(DMLabelGetValueIS(label, &valueIS));
1525       PetscCall(ISGetIndices(valueIS, &values));
1526       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1527       for (v = 0; v < numValues; ++v) {
1528         PetscInt size;
1529 
1530         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1531         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1532         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1533       }
1534       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1535       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1536       PetscCall(ISRestoreIndices(valueIS, &values));
1537       PetscCall(ISDestroy(&valueIS));
1538     }
1539     {
1540       char    **labelNames;
1541       PetscInt  Nl = numLabels;
1542       PetscBool flg;
1543 
1544       PetscCall(PetscMalloc1(Nl, &labelNames));
1545       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1546       for (l = 0; l < Nl; ++l) {
1547         DMLabel label;
1548 
1549         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1550         if (flg) {
1551           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1552           PetscCall(DMLabelView(label, viewer));
1553         }
1554         PetscCall(PetscFree(labelNames[l]));
1555       }
1556       PetscCall(PetscFree(labelNames));
1557     }
1558     /* If no fields are specified, people do not want to see adjacency */
1559     if (dm->Nf) {
1560       PetscInt f;
1561 
1562       for (f = 0; f < dm->Nf; ++f) {
1563         const char *name;
1564 
1565         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1566         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1567         PetscCall(PetscViewerASCIIPushTab(viewer));
1568         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1569         if (dm->fields[f].adjacency[0]) {
1570           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1571           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1572         } else {
1573           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1574           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1575         }
1576         PetscCall(PetscViewerASCIIPopTab(viewer));
1577       }
1578     }
1579     PetscCall(DMGetCoarseDM(dm, &cdm));
1580     if (cdm) {
1581       PetscCall(PetscViewerASCIIPushTab(viewer));
1582       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1583       PetscCall(DMPlexView_Ascii(cdm, viewer));
1584       PetscCall(PetscViewerASCIIPopTab(viewer));
1585     }
1586   }
1587   PetscFunctionReturn(PETSC_SUCCESS);
1588 }
1589 
1590 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1591 {
1592   DMPolytopeType ct;
1593   PetscMPIInt    rank;
1594   PetscInt       cdim;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   PetscCall(DMGetCoordinateDim(dm, &cdim));
1600   switch (ct) {
1601   case DM_POLYTOPE_SEGMENT:
1602   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1603     switch (cdim) {
1604     case 1: {
1605       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1606       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1607 
1608       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1609       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1610       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1611     } break;
1612     case 2: {
1613       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1614       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1615       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1616 
1617       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1618       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1619       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1620     } break;
1621     default:
1622       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1623     }
1624     break;
1625   case DM_POLYTOPE_TRIANGLE:
1626     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1627     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1628     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1629     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1630     break;
1631   case DM_POLYTOPE_QUADRILATERAL:
1632     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1633     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1635     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1636     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1637     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1638     break;
1639   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1640     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1641     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1642     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1643     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1644     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1645     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1646     break;
1647   case DM_POLYTOPE_FV_GHOST:
1648     break;
1649   default:
1650     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1651   }
1652   PetscFunctionReturn(PETSC_SUCCESS);
1653 }
1654 
1655 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1656 {
1657   DMPolytopeType ct;
1658   PetscReal      centroid[2] = {0., 0.};
1659   PetscMPIInt    rank;
1660   PetscInt       fillColor, v, e, d;
1661 
1662   PetscFunctionBegin;
1663   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1664   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1665   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1666   switch (ct) {
1667   case DM_POLYTOPE_TRIANGLE: {
1668     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1669 
1670     for (v = 0; v < 3; ++v) {
1671       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1672       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1673     }
1674     for (e = 0; e < 3; ++e) {
1675       refCoords[0] = refVertices[e * 2 + 0];
1676       refCoords[1] = refVertices[e * 2 + 1];
1677       for (d = 1; d <= edgeDiv; ++d) {
1678         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1679         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1680       }
1681       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1682       for (d = 0; d < edgeDiv; ++d) {
1683         PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1684         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1685       }
1686     }
1687   } break;
1688   default:
1689     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1690   }
1691   PetscFunctionReturn(PETSC_SUCCESS);
1692 }
1693 
1694 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1695 {
1696   PetscDraw    draw;
1697   DM           cdm;
1698   PetscSection coordSection;
1699   Vec          coordinates;
1700   PetscReal    xyl[3], xyr[3];
1701   PetscReal   *refCoords, *edgeCoords;
1702   PetscBool    isnull, drawAffine = PETSC_TRUE;
1703   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1704 
1705   PetscFunctionBegin;
1706   PetscCall(DMGetCoordinateDim(dm, &dim));
1707   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1708   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1709   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1710   PetscCall(DMGetCoordinateDM(dm, &cdm));
1711   PetscCall(DMGetLocalSection(cdm, &coordSection));
1712   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1713   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1714   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1715 
1716   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1717   PetscCall(PetscDrawIsNull(draw, &isnull));
1718   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1719   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1720 
1721   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1722   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1723   PetscCall(PetscDrawClear(draw));
1724 
1725   for (c = cStart; c < cEnd; ++c) {
1726     PetscScalar       *coords = NULL;
1727     const PetscScalar *coords_arr;
1728     PetscInt           numCoords;
1729     PetscBool          isDG;
1730 
1731     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1732     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1733     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1734     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1735   }
1736   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1737   PetscCall(PetscDrawFlush(draw));
1738   PetscCall(PetscDrawPause(draw));
1739   PetscCall(PetscDrawSave(draw));
1740   PetscFunctionReturn(PETSC_SUCCESS);
1741 }
1742 
1743 #if defined(PETSC_HAVE_EXODUSII)
1744   #include <exodusII.h>
1745   #include <petscviewerexodusii.h>
1746 #endif
1747 
1748 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1749 {
1750   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1751   char      name[PETSC_MAX_PATH_LEN];
1752 
1753   PetscFunctionBegin;
1754   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1755   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1756   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1757   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1759   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1761   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1762   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1763   if (iascii) {
1764     PetscViewerFormat format;
1765     PetscCall(PetscViewerGetFormat(viewer, &format));
1766     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1767     else PetscCall(DMPlexView_Ascii(dm, viewer));
1768   } else if (ishdf5) {
1769 #if defined(PETSC_HAVE_HDF5)
1770     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1771 #else
1772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1773 #endif
1774   } else if (isvtk) {
1775     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1776   } else if (isdraw) {
1777     PetscCall(DMPlexView_Draw(dm, viewer));
1778   } else if (isglvis) {
1779     PetscCall(DMPlexView_GLVis(dm, viewer));
1780 #if defined(PETSC_HAVE_EXODUSII)
1781   } else if (isexodus) {
1782     /*
1783       exodusII requires that all sets be part of exactly one cell set.
1784       If the dm does not have a "Cell Sets" label defined, we create one
1785       with ID 1, containing all cells.
1786       Note that if the Cell Sets label is defined but does not cover all cells,
1787       we may still have a problem. This should probably be checked here or in the viewer;
1788     */
1789     PetscInt numCS;
1790     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1791     if (!numCS) {
1792       PetscInt cStart, cEnd, c;
1793       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1794       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1795       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1796     }
1797     PetscCall(DMView_PlexExodusII(dm, viewer));
1798 #endif
1799 #if defined(PETSC_HAVE_CGNS)
1800   } else if (iscgns) {
1801     PetscCall(DMView_PlexCGNS(dm, viewer));
1802 #endif
1803   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1804   /* Optionally view the partition */
1805   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1806   if (flg) {
1807     Vec ranks;
1808     PetscCall(DMPlexCreateRankField(dm, &ranks));
1809     PetscCall(VecView(ranks, viewer));
1810     PetscCall(VecDestroy(&ranks));
1811   }
1812   /* Optionally view a label */
1813   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1814   if (flg) {
1815     DMLabel label;
1816     Vec     val;
1817 
1818     PetscCall(DMGetLabel(dm, name, &label));
1819     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1820     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1821     PetscCall(VecView(val, viewer));
1822     PetscCall(VecDestroy(&val));
1823   }
1824   PetscFunctionReturn(PETSC_SUCCESS);
1825 }
1826 
1827 /*@
1828   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1829 
1830   Collective
1831 
1832   Input Parameters:
1833 + dm     - The `DM` whose topology is to be saved
1834 - viewer - The `PetscViewer` to save it in
1835 
1836   Level: advanced
1837 
1838 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1839 @*/
1840 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1841 {
1842   PetscBool ishdf5;
1843 
1844   PetscFunctionBegin;
1845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1846   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1847   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1848   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1849   if (ishdf5) {
1850 #if defined(PETSC_HAVE_HDF5)
1851     PetscViewerFormat format;
1852     PetscCall(PetscViewerGetFormat(viewer, &format));
1853     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1854       IS globalPointNumbering;
1855 
1856       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1857       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1858       PetscCall(ISDestroy(&globalPointNumbering));
1859     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1860 #else
1861     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1862 #endif
1863   }
1864   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 /*@
1869   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1870 
1871   Collective
1872 
1873   Input Parameters:
1874 + dm     - The `DM` whose coordinates are to be saved
1875 - viewer - The `PetscViewer` for saving
1876 
1877   Level: advanced
1878 
1879 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1880 @*/
1881 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1882 {
1883   PetscBool ishdf5;
1884 
1885   PetscFunctionBegin;
1886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1887   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1888   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1889   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1890   if (ishdf5) {
1891 #if defined(PETSC_HAVE_HDF5)
1892     PetscViewerFormat format;
1893     PetscCall(PetscViewerGetFormat(viewer, &format));
1894     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1895       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1896     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1897 #else
1898     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1899 #endif
1900   }
1901   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1902   PetscFunctionReturn(PETSC_SUCCESS);
1903 }
1904 
1905 /*@
1906   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1907 
1908   Collective
1909 
1910   Input Parameters:
1911 + dm     - The `DM` whose labels are to be saved
1912 - viewer - The `PetscViewer` for saving
1913 
1914   Level: advanced
1915 
1916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1917 @*/
1918 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1919 {
1920   PetscBool ishdf5;
1921 
1922   PetscFunctionBegin;
1923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1924   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1925   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1926   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1927   if (ishdf5) {
1928 #if defined(PETSC_HAVE_HDF5)
1929     IS                globalPointNumbering;
1930     PetscViewerFormat format;
1931 
1932     PetscCall(PetscViewerGetFormat(viewer, &format));
1933     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1934       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1935       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1936       PetscCall(ISDestroy(&globalPointNumbering));
1937     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1938 #else
1939     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1940 #endif
1941   }
1942   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1943   PetscFunctionReturn(PETSC_SUCCESS);
1944 }
1945 
1946 /*@
1947   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1948 
1949   Collective
1950 
1951   Input Parameters:
1952 + dm        - The `DM` that contains the topology on which the section to be saved is defined
1953 . viewer    - The `PetscViewer` for saving
1954 - sectiondm - The `DM` that contains the section to be saved
1955 
1956   Level: advanced
1957 
1958   Notes:
1959   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
1960 
1961   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1962 
1963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1964 @*/
1965 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1966 {
1967   PetscBool ishdf5;
1968 
1969   PetscFunctionBegin;
1970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1971   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1972   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1973   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1974   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1975   if (ishdf5) {
1976 #if defined(PETSC_HAVE_HDF5)
1977     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1978 #else
1979     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1980 #endif
1981   }
1982   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1983   PetscFunctionReturn(PETSC_SUCCESS);
1984 }
1985 
1986 /*@
1987   DMPlexGlobalVectorView - Saves a global vector
1988 
1989   Collective
1990 
1991   Input Parameters:
1992 + dm        - The `DM` that represents the topology
1993 . viewer    - The `PetscViewer` to save data with
1994 . sectiondm - The `DM` that contains the global section on which vec is defined
1995 - vec       - The global vector to be saved
1996 
1997   Level: advanced
1998 
1999   Notes:
2000   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2001 
2002   Calling sequence:
2003 .vb
2004        DMCreate(PETSC_COMM_WORLD, &dm);
2005        DMSetType(dm, DMPLEX);
2006        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2007        DMClone(dm, &sectiondm);
2008        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2009        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2010        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2011        PetscSectionSetChart(section, pStart, pEnd);
2012        PetscSectionSetUp(section);
2013        DMSetLocalSection(sectiondm, section);
2014        PetscSectionDestroy(&section);
2015        DMGetGlobalVector(sectiondm, &vec);
2016        PetscObjectSetName((PetscObject)vec, "vec_name");
2017        DMPlexTopologyView(dm, viewer);
2018        DMPlexSectionView(dm, viewer, sectiondm);
2019        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2020        DMRestoreGlobalVector(sectiondm, &vec);
2021        DMDestroy(&sectiondm);
2022        DMDestroy(&dm);
2023 .ve
2024 
2025 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2026 @*/
2027 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2028 {
2029   PetscBool ishdf5;
2030 
2031   PetscFunctionBegin;
2032   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2033   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2034   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2035   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2036   /* Check consistency */
2037   {
2038     PetscSection section;
2039     PetscBool    includesConstraints;
2040     PetscInt     m, m1;
2041 
2042     PetscCall(VecGetLocalSize(vec, &m1));
2043     PetscCall(DMGetGlobalSection(sectiondm, &section));
2044     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2045     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2046     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2047     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2048   }
2049   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2050   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2051   if (ishdf5) {
2052 #if defined(PETSC_HAVE_HDF5)
2053     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2054 #else
2055     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2056 #endif
2057   }
2058   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2059   PetscFunctionReturn(PETSC_SUCCESS);
2060 }
2061 
2062 /*@
2063   DMPlexLocalVectorView - Saves a local vector
2064 
2065   Collective
2066 
2067   Input Parameters:
2068 + dm        - The `DM` that represents the topology
2069 . viewer    - The `PetscViewer` to save data with
2070 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2071 - vec       - The local vector to be saved
2072 
2073   Level: advanced
2074 
2075   Note:
2076   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2077 
2078   Calling sequence:
2079 .vb
2080        DMCreate(PETSC_COMM_WORLD, &dm);
2081        DMSetType(dm, DMPLEX);
2082        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2083        DMClone(dm, &sectiondm);
2084        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2085        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2086        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2087        PetscSectionSetChart(section, pStart, pEnd);
2088        PetscSectionSetUp(section);
2089        DMSetLocalSection(sectiondm, section);
2090        DMGetLocalVector(sectiondm, &vec);
2091        PetscObjectSetName((PetscObject)vec, "vec_name");
2092        DMPlexTopologyView(dm, viewer);
2093        DMPlexSectionView(dm, viewer, sectiondm);
2094        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2095        DMRestoreLocalVector(sectiondm, &vec);
2096        DMDestroy(&sectiondm);
2097        DMDestroy(&dm);
2098 .ve
2099 
2100 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2101 @*/
2102 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2103 {
2104   PetscBool ishdf5;
2105 
2106   PetscFunctionBegin;
2107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2108   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2109   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2110   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2111   /* Check consistency */
2112   {
2113     PetscSection section;
2114     PetscBool    includesConstraints;
2115     PetscInt     m, m1;
2116 
2117     PetscCall(VecGetLocalSize(vec, &m1));
2118     PetscCall(DMGetLocalSection(sectiondm, &section));
2119     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2120     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2121     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2122     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2123   }
2124   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2125   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2126   if (ishdf5) {
2127 #if defined(PETSC_HAVE_HDF5)
2128     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2129 #else
2130     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2131 #endif
2132   }
2133   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2134   PetscFunctionReturn(PETSC_SUCCESS);
2135 }
2136 
2137 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2138 {
2139   PetscBool ishdf5;
2140 
2141   PetscFunctionBegin;
2142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2143   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2144   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2145   if (ishdf5) {
2146 #if defined(PETSC_HAVE_HDF5)
2147     PetscViewerFormat format;
2148     PetscCall(PetscViewerGetFormat(viewer, &format));
2149     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2150       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2151     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2152       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2153     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2154     PetscFunctionReturn(PETSC_SUCCESS);
2155 #else
2156     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2157 #endif
2158   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2159 }
2160 
2161 /*@
2162   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2163 
2164   Collective
2165 
2166   Input Parameters:
2167 + dm     - The `DM` into which the topology is loaded
2168 - viewer - The `PetscViewer` for the saved topology
2169 
2170   Output Parameter:
2171 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2172 
2173   Level: advanced
2174 
2175 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2176           `PetscViewer`, `PetscSF`
2177 @*/
2178 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2179 {
2180   PetscBool ishdf5;
2181 
2182   PetscFunctionBegin;
2183   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2184   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2185   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2186   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2187   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2188   if (ishdf5) {
2189 #if defined(PETSC_HAVE_HDF5)
2190     PetscViewerFormat format;
2191     PetscCall(PetscViewerGetFormat(viewer, &format));
2192     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2193       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2194     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2195 #else
2196     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2197 #endif
2198   }
2199   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2200   PetscFunctionReturn(PETSC_SUCCESS);
2201 }
2202 
2203 /*@
2204   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2205 
2206   Collective
2207 
2208   Input Parameters:
2209 + dm                   - The `DM` into which the coordinates are loaded
2210 . viewer               - The `PetscViewer` for the saved coordinates
2211 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2212 
2213   Level: advanced
2214 
2215 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2216           `PetscSF`, `PetscViewer`
2217 @*/
2218 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2219 {
2220   PetscBool ishdf5;
2221 
2222   PetscFunctionBegin;
2223   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2224   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2225   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2226   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2227   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2228   if (ishdf5) {
2229 #if defined(PETSC_HAVE_HDF5)
2230     PetscViewerFormat format;
2231     PetscCall(PetscViewerGetFormat(viewer, &format));
2232     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2233       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2234     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2235 #else
2236     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2237 #endif
2238   }
2239   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2240   PetscFunctionReturn(PETSC_SUCCESS);
2241 }
2242 
2243 /*@
2244   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2245 
2246   Collective
2247 
2248   Input Parameters:
2249 + dm                   - The `DM` into which the labels are loaded
2250 . viewer               - The `PetscViewer` for the saved labels
2251 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2252 
2253   Level: advanced
2254 
2255   Note:
2256   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2257 
2258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2259           `PetscSF`, `PetscViewer`
2260 @*/
2261 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2262 {
2263   PetscBool ishdf5;
2264 
2265   PetscFunctionBegin;
2266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2267   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2268   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2269   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2270   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2271   if (ishdf5) {
2272 #if defined(PETSC_HAVE_HDF5)
2273     PetscViewerFormat format;
2274 
2275     PetscCall(PetscViewerGetFormat(viewer, &format));
2276     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2277       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2278     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2279 #else
2280     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2281 #endif
2282   }
2283   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2284   PetscFunctionReturn(PETSC_SUCCESS);
2285 }
2286 
2287 /*@
2288   DMPlexSectionLoad - Loads section into a `DMPLEX`
2289 
2290   Collective
2291 
2292   Input Parameters:
2293 + dm                   - The `DM` that represents the topology
2294 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2295 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated
2296 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2297 
2298   Output Parameters:
2299 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2300 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2301 
2302   Level: advanced
2303 
2304   Notes:
2305   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2306 
2307   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2308 
2309   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2310 
2311   Example using 2 processes:
2312 .vb
2313   NX (number of points on dm): 4
2314   sectionA                   : the on-disk section
2315   vecA                       : a vector associated with sectionA
2316   sectionB                   : sectiondm's local section constructed in this function
2317   vecB (local)               : a vector associated with sectiondm's local section
2318   vecB (global)              : a vector associated with sectiondm's global section
2319 
2320                                      rank 0    rank 1
2321   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2322   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2323   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2324   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2325   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2326   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2327   sectionB->atlasDof             :     1 0 1 | 1 3
2328   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2329   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2330   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2331 .ve
2332   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2333 
2334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2335 @*/
2336 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2344   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2345   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2346   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2347   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2348   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2349   if (ishdf5) {
2350 #if defined(PETSC_HAVE_HDF5)
2351     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2352 #else
2353     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2354 #endif
2355   }
2356   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2357   PetscFunctionReturn(PETSC_SUCCESS);
2358 }
2359 
2360 /*@
2361   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2362 
2363   Collective
2364 
2365   Input Parameters:
2366 + dm        - The `DM` that represents the topology
2367 . viewer    - The `PetscViewer` that represents the on-disk vector data
2368 . sectiondm - The `DM` that contains the global section on which vec is defined
2369 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2370 - vec       - The global vector to set values of
2371 
2372   Level: advanced
2373 
2374   Notes:
2375   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2376 
2377   Calling sequence:
2378 .vb
2379        DMCreate(PETSC_COMM_WORLD, &dm);
2380        DMSetType(dm, DMPLEX);
2381        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2382        DMPlexTopologyLoad(dm, viewer, &sfX);
2383        DMClone(dm, &sectiondm);
2384        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2385        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2386        DMGetGlobalVector(sectiondm, &vec);
2387        PetscObjectSetName((PetscObject)vec, "vec_name");
2388        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2389        DMRestoreGlobalVector(sectiondm, &vec);
2390        PetscSFDestroy(&gsf);
2391        PetscSFDestroy(&sfX);
2392        DMDestroy(&sectiondm);
2393        DMDestroy(&dm);
2394 .ve
2395 
2396 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2397           `PetscSF`, `PetscViewer`
2398 @*/
2399 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2400 {
2401   PetscBool ishdf5;
2402 
2403   PetscFunctionBegin;
2404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2405   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2406   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2407   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2408   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2409   /* Check consistency */
2410   {
2411     PetscSection section;
2412     PetscBool    includesConstraints;
2413     PetscInt     m, m1;
2414 
2415     PetscCall(VecGetLocalSize(vec, &m1));
2416     PetscCall(DMGetGlobalSection(sectiondm, &section));
2417     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2418     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2419     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2420     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2421   }
2422   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2423   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2424   if (ishdf5) {
2425 #if defined(PETSC_HAVE_HDF5)
2426     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2427 #else
2428     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2429 #endif
2430   }
2431   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2432   PetscFunctionReturn(PETSC_SUCCESS);
2433 }
2434 
2435 /*@
2436   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2437 
2438   Collective
2439 
2440   Input Parameters:
2441 + dm        - The `DM` that represents the topology
2442 . viewer    - The `PetscViewer` that represents the on-disk vector data
2443 . sectiondm - The `DM` that contains the local section on which vec is defined
2444 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2445 - vec       - The local vector to set values of
2446 
2447   Level: advanced
2448 
2449   Notes:
2450   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2451 
2452   Calling sequence:
2453 .vb
2454        DMCreate(PETSC_COMM_WORLD, &dm);
2455        DMSetType(dm, DMPLEX);
2456        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2457        DMPlexTopologyLoad(dm, viewer, &sfX);
2458        DMClone(dm, &sectiondm);
2459        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2460        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2461        DMGetLocalVector(sectiondm, &vec);
2462        PetscObjectSetName((PetscObject)vec, "vec_name");
2463        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2464        DMRestoreLocalVector(sectiondm, &vec);
2465        PetscSFDestroy(&lsf);
2466        PetscSFDestroy(&sfX);
2467        DMDestroy(&sectiondm);
2468        DMDestroy(&dm);
2469 .ve
2470 
2471 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2472           `PetscSF`, `PetscViewer`
2473 @*/
2474 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2475 {
2476   PetscBool ishdf5;
2477 
2478   PetscFunctionBegin;
2479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2480   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2481   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2482   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2483   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2484   /* Check consistency */
2485   {
2486     PetscSection section;
2487     PetscBool    includesConstraints;
2488     PetscInt     m, m1;
2489 
2490     PetscCall(VecGetLocalSize(vec, &m1));
2491     PetscCall(DMGetLocalSection(sectiondm, &section));
2492     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2493     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2494     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2495     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2496   }
2497   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2498   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2499   if (ishdf5) {
2500 #if defined(PETSC_HAVE_HDF5)
2501     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2502 #else
2503     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2504 #endif
2505   }
2506   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2507   PetscFunctionReturn(PETSC_SUCCESS);
2508 }
2509 
2510 PetscErrorCode DMDestroy_Plex(DM dm)
2511 {
2512   DM_Plex *mesh = (DM_Plex *)dm->data;
2513 
2514   PetscFunctionBegin;
2515   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2516   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2517   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2518   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2519   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2520   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2521   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2522   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2523   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2524   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2525   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2526   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2527   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2528   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2529   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2530   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2531   PetscCall(PetscFree(mesh->cones));
2532   PetscCall(PetscFree(mesh->coneOrientations));
2533   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2534   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2535   PetscCall(PetscFree(mesh->supports));
2536   PetscCall(PetscFree(mesh->cellTypes));
2537   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2538   PetscCall(PetscFree(mesh->tetgenOpts));
2539   PetscCall(PetscFree(mesh->triangleOpts));
2540   PetscCall(PetscFree(mesh->transformType));
2541   PetscCall(PetscFree(mesh->distributionName));
2542   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2543   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2544   PetscCall(ISDestroy(&mesh->subpointIS));
2545   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2546   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2547   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2548   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2549   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2550   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2551   PetscCall(ISDestroy(&mesh->anchorIS));
2552   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2553   PetscCall(PetscFree(mesh->parents));
2554   PetscCall(PetscFree(mesh->childIDs));
2555   PetscCall(PetscSectionDestroy(&mesh->childSection));
2556   PetscCall(PetscFree(mesh->children));
2557   PetscCall(DMDestroy(&mesh->referenceTree));
2558   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2559   PetscCall(PetscFree(mesh->neighbors));
2560   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2561   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2562   PetscCall(PetscFree(mesh));
2563   PetscFunctionReturn(PETSC_SUCCESS);
2564 }
2565 
2566 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2567 {
2568   PetscSection           sectionGlobal;
2569   PetscInt               bs = -1, mbs;
2570   PetscInt               localSize, localStart = 0;
2571   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2572   MatType                mtype;
2573   ISLocalToGlobalMapping ltog;
2574 
2575   PetscFunctionBegin;
2576   PetscCall(MatInitializePackage());
2577   mtype = dm->mattype;
2578   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2579   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2580   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2581   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2582   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2583   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2584   PetscCall(MatSetType(*J, mtype));
2585   PetscCall(MatSetFromOptions(*J));
2586   PetscCall(MatGetBlockSize(*J, &mbs));
2587   if (mbs > 1) bs = mbs;
2588   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2589   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2590   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2591   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2592   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2593   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2594   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2595   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2596   if (!isShell) {
2597     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2598     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2599     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2600 
2601     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2602 
2603     PetscCall(PetscCalloc1(localSize, &pblocks));
2604     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2605     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2606     for (p = pStart; p < pEnd; ++p) {
2607       switch (dm->blocking_type) {
2608       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2609         PetscInt bdof, offset;
2610 
2611         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2612         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2613         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2614         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2615         dof  = dof < 0 ? -(dof + 1) : dof;
2616         bdof = cdof && (dof - cdof) ? 1 : dof;
2617         if (dof) {
2618           if (bs < 0) {
2619             bs = bdof;
2620           } else if (bs != bdof) {
2621             bs = 1;
2622           }
2623         }
2624       } break;
2625       case DM_BLOCKING_FIELD_NODE: {
2626         for (PetscInt field = 0; field < num_fields; field++) {
2627           PetscInt num_comp, bdof, offset;
2628           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2629           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2630           if (dof < 0) continue;
2631           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2632           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2633           PetscAssert(dof % num_comp == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " field %" PetscInt_FMT " has %" PetscInt_FMT " dof, not divisible by %" PetscInt_FMT " component ", p, field, dof, num_comp);
2634           PetscInt num_nodes = dof / num_comp;
2635           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2636           // Handle possibly constant block size (unlikely)
2637           bdof = cdof && (dof - cdof) ? 1 : dof;
2638           if (dof) {
2639             if (bs < 0) {
2640               bs = bdof;
2641             } else if (bs != bdof) {
2642               bs = 1;
2643             }
2644           }
2645         }
2646       } break;
2647       }
2648     }
2649     /* Must have same blocksize on all procs (some might have no points) */
2650     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2651     bsLocal[1] = bs;
2652     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2653     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2654     else bs = bsMinMax[0];
2655     bs = PetscMax(1, bs);
2656     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2657     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2658       PetscCall(MatSetBlockSize(*J, bs));
2659       PetscCall(MatSetUp(*J));
2660     } else {
2661       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2662       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2663       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2664     }
2665     { // Consolidate blocks
2666       PetscInt nblocks = 0;
2667       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2668         if (pblocks[i] == 0) continue;
2669         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2670         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2671       }
2672       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2673     }
2674     PetscCall(PetscFree(pblocks));
2675   }
2676   PetscCall(MatSetDM(*J, dm));
2677   PetscFunctionReturn(PETSC_SUCCESS);
2678 }
2679 
2680 /*@
2681   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2682 
2683   Not Collective
2684 
2685   Input Parameter:
2686 . dm - The `DMPLEX`
2687 
2688   Output Parameter:
2689 . subsection - The subdomain section
2690 
2691   Level: developer
2692 
2693 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2694 @*/
2695 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2696 {
2697   DM_Plex *mesh = (DM_Plex *)dm->data;
2698 
2699   PetscFunctionBegin;
2700   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2701   if (!mesh->subdomainSection) {
2702     PetscSection section;
2703     PetscSF      sf;
2704 
2705     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2706     PetscCall(DMGetLocalSection(dm, &section));
2707     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2708     PetscCall(PetscSFDestroy(&sf));
2709   }
2710   *subsection = mesh->subdomainSection;
2711   PetscFunctionReturn(PETSC_SUCCESS);
2712 }
2713 
2714 /*@
2715   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2716 
2717   Not Collective
2718 
2719   Input Parameter:
2720 . dm - The `DMPLEX`
2721 
2722   Output Parameters:
2723 + pStart - The first mesh point
2724 - pEnd   - The upper bound for mesh points
2725 
2726   Level: beginner
2727 
2728 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2729 @*/
2730 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2731 {
2732   DM_Plex *mesh = (DM_Plex *)dm->data;
2733 
2734   PetscFunctionBegin;
2735   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2736   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2737   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2738   PetscFunctionReturn(PETSC_SUCCESS);
2739 }
2740 
2741 /*@
2742   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2743 
2744   Not Collective
2745 
2746   Input Parameters:
2747 + dm     - The `DMPLEX`
2748 . pStart - The first mesh point
2749 - pEnd   - The upper bound for mesh points
2750 
2751   Level: beginner
2752 
2753 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2754 @*/
2755 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2756 {
2757   DM_Plex *mesh = (DM_Plex *)dm->data;
2758 
2759   PetscFunctionBegin;
2760   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2761   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2762   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2763   PetscCall(PetscFree(mesh->cellTypes));
2764   PetscFunctionReturn(PETSC_SUCCESS);
2765 }
2766 
2767 /*@
2768   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2769 
2770   Not Collective
2771 
2772   Input Parameters:
2773 + dm - The `DMPLEX`
2774 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2775 
2776   Output Parameter:
2777 . size - The cone size for point `p`
2778 
2779   Level: beginner
2780 
2781 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2782 @*/
2783 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2784 {
2785   DM_Plex *mesh = (DM_Plex *)dm->data;
2786 
2787   PetscFunctionBegin;
2788   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2789   PetscAssertPointer(size, 3);
2790   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2791   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2792   PetscFunctionReturn(PETSC_SUCCESS);
2793 }
2794 
2795 /*@
2796   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2797 
2798   Not Collective
2799 
2800   Input Parameters:
2801 + dm   - The `DMPLEX`
2802 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2803 - size - The cone size for point `p`
2804 
2805   Level: beginner
2806 
2807   Note:
2808   This should be called after `DMPlexSetChart()`.
2809 
2810 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2811 @*/
2812 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2813 {
2814   DM_Plex *mesh = (DM_Plex *)dm->data;
2815 
2816   PetscFunctionBegin;
2817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2818   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2819   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2820   PetscFunctionReturn(PETSC_SUCCESS);
2821 }
2822 
2823 /*@C
2824   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2825 
2826   Not Collective
2827 
2828   Input Parameters:
2829 + dm - The `DMPLEX`
2830 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2831 
2832   Output Parameter:
2833 . cone - An array of points which are on the in-edges for point `p`
2834 
2835   Level: beginner
2836 
2837   Fortran Notes:
2838   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2839   `DMPlexRestoreCone()` is not needed/available in C.
2840 
2841 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2842 @*/
2843 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2844 {
2845   DM_Plex *mesh = (DM_Plex *)dm->data;
2846   PetscInt off;
2847 
2848   PetscFunctionBegin;
2849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2850   PetscAssertPointer(cone, 3);
2851   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2852   *cone = &mesh->cones[off];
2853   PetscFunctionReturn(PETSC_SUCCESS);
2854 }
2855 
2856 /*@C
2857   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2858 
2859   Not Collective
2860 
2861   Input Parameters:
2862 + dm - The `DMPLEX`
2863 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2864 
2865   Output Parameters:
2866 + pConesSection - `PetscSection` describing the layout of `pCones`
2867 - pCones        - An array of points which are on the in-edges for the point set `p`
2868 
2869   Level: intermediate
2870 
2871 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2872 @*/
2873 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2874 {
2875   PetscSection cs, newcs;
2876   PetscInt    *cones;
2877   PetscInt    *newarr = NULL;
2878   PetscInt     n;
2879 
2880   PetscFunctionBegin;
2881   PetscCall(DMPlexGetCones(dm, &cones));
2882   PetscCall(DMPlexGetConeSection(dm, &cs));
2883   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2884   if (pConesSection) *pConesSection = newcs;
2885   if (pCones) {
2886     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2887     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2888   }
2889   PetscFunctionReturn(PETSC_SUCCESS);
2890 }
2891 
2892 /*@
2893   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2894 
2895   Not Collective
2896 
2897   Input Parameters:
2898 + dm     - The `DMPLEX`
2899 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2900 
2901   Output Parameter:
2902 . expandedPoints - An array of vertices recursively expanded from input points
2903 
2904   Level: advanced
2905 
2906   Notes:
2907   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
2908 
2909   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2910 
2911 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2912           `DMPlexGetDepth()`, `IS`
2913 @*/
2914 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2915 {
2916   IS      *expandedPointsAll;
2917   PetscInt depth;
2918 
2919   PetscFunctionBegin;
2920   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2921   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2922   PetscAssertPointer(expandedPoints, 3);
2923   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2924   *expandedPoints = expandedPointsAll[0];
2925   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2926   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2927   PetscFunctionReturn(PETSC_SUCCESS);
2928 }
2929 
2930 /*@
2931   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
2932 
2933   Not Collective
2934 
2935   Input Parameters:
2936 + dm     - The `DMPLEX`
2937 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2938 
2939   Output Parameters:
2940 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2941 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2942 - sections       - (optional) An array of sections which describe mappings from points to their cone points
2943 
2944   Level: advanced
2945 
2946   Notes:
2947   Like `DMPlexGetConeTuple()` but recursive.
2948 
2949   Array `expandedPoints` has size equal to `depth`. Each `expandedPoints`[d] contains DAG points with maximum depth d, recursively cone-wise expanded from the input points.
2950   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2951 
2952   Array section has size equal to `depth`.  Each `PetscSection` `sections`[d] realizes mapping from `expandedPoints`[d+1] (section points) to `expandedPoints`[d] (section dofs) as follows\:
2953   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
2954   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
2955 
2956 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2957           `DMPlexGetDepth()`, `PetscSection`, `IS`
2958 @*/
2959 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2960 {
2961   const PetscInt *arr0 = NULL, *cone = NULL;
2962   PetscInt       *arr = NULL, *newarr = NULL;
2963   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2964   IS             *expandedPoints_;
2965   PetscSection   *sections_;
2966 
2967   PetscFunctionBegin;
2968   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2969   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2970   if (depth) PetscAssertPointer(depth, 3);
2971   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
2972   if (sections) PetscAssertPointer(sections, 5);
2973   PetscCall(ISGetLocalSize(points, &n));
2974   PetscCall(ISGetIndices(points, &arr0));
2975   PetscCall(DMPlexGetDepth(dm, &depth_));
2976   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2977   PetscCall(PetscCalloc1(depth_, &sections_));
2978   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2979   for (d = depth_ - 1; d >= 0; d--) {
2980     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2981     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2982     for (i = 0; i < n; i++) {
2983       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2984       if (arr[i] >= start && arr[i] < end) {
2985         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2986         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2987       } else {
2988         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2989       }
2990     }
2991     PetscCall(PetscSectionSetUp(sections_[d]));
2992     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2993     PetscCall(PetscMalloc1(newn, &newarr));
2994     for (i = 0; i < n; i++) {
2995       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2996       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2997       if (cn > 1) {
2998         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2999         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3000       } else {
3001         newarr[co] = arr[i];
3002       }
3003     }
3004     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3005     arr = newarr;
3006     n   = newn;
3007   }
3008   PetscCall(ISRestoreIndices(points, &arr0));
3009   *depth = depth_;
3010   if (expandedPoints) *expandedPoints = expandedPoints_;
3011   else {
3012     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3013     PetscCall(PetscFree(expandedPoints_));
3014   }
3015   if (sections) *sections = sections_;
3016   else {
3017     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3018     PetscCall(PetscFree(sections_));
3019   }
3020   PetscFunctionReturn(PETSC_SUCCESS);
3021 }
3022 
3023 /*@
3024   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3025 
3026   Not Collective
3027 
3028   Input Parameters:
3029 + dm     - The `DMPLEX`
3030 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3031 
3032   Output Parameters:
3033 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3034 . expandedPoints - (optional) An array of recursively expanded cones
3035 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3036 
3037   Level: advanced
3038 
3039   Note:
3040   See `DMPlexGetConeRecursive()`
3041 
3042 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3043           `DMPlexGetDepth()`, `IS`, `PetscSection`
3044 @*/
3045 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3046 {
3047   PetscInt d, depth_;
3048 
3049   PetscFunctionBegin;
3050   PetscCall(DMPlexGetDepth(dm, &depth_));
3051   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3052   if (depth) *depth = 0;
3053   if (expandedPoints) {
3054     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3055     PetscCall(PetscFree(*expandedPoints));
3056   }
3057   if (sections) {
3058     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3059     PetscCall(PetscFree(*sections));
3060   }
3061   PetscFunctionReturn(PETSC_SUCCESS);
3062 }
3063 
3064 /*@
3065   DMPlexSetCone - Set the points on the in-edges for this point in the DAG; that is these are the points that cover the specific point
3066 
3067   Not Collective
3068 
3069   Input Parameters:
3070 + dm   - The `DMPLEX`
3071 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3072 - cone - An array of points which are on the in-edges for point `p`
3073 
3074   Level: beginner
3075 
3076   Note:
3077   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3078 
3079 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3080 @*/
3081 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3082 {
3083   DM_Plex *mesh = (DM_Plex *)dm->data;
3084   PetscInt dof, off, c;
3085 
3086   PetscFunctionBegin;
3087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3088   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3089   if (dof) PetscAssertPointer(cone, 3);
3090   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3091   if (PetscDefined(USE_DEBUG)) {
3092     PetscInt pStart, pEnd;
3093     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3094     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3095     for (c = 0; c < dof; ++c) {
3096       PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
3097       mesh->cones[off + c] = cone[c];
3098     }
3099   } else {
3100     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3101   }
3102   PetscFunctionReturn(PETSC_SUCCESS);
3103 }
3104 
3105 /*@C
3106   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3107 
3108   Not Collective
3109 
3110   Input Parameters:
3111 + dm - The `DMPLEX`
3112 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3113 
3114   Output Parameter:
3115 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3116                     integer giving the prescription for cone traversal.
3117 
3118   Level: beginner
3119 
3120   Note:
3121   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3122   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3123   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3124   with the identity.
3125 
3126   Fortran Notes:
3127   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3128   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3129 
3130 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3131 @*/
3132 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3133 {
3134   DM_Plex *mesh = (DM_Plex *)dm->data;
3135   PetscInt off;
3136 
3137   PetscFunctionBegin;
3138   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3139   if (PetscDefined(USE_DEBUG)) {
3140     PetscInt dof;
3141     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3142     if (dof) PetscAssertPointer(coneOrientation, 3);
3143   }
3144   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3145 
3146   *coneOrientation = &mesh->coneOrientations[off];
3147   PetscFunctionReturn(PETSC_SUCCESS);
3148 }
3149 
3150 /*@
3151   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3152 
3153   Not Collective
3154 
3155   Input Parameters:
3156 + dm              - The `DMPLEX`
3157 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3158 - coneOrientation - An array of orientations
3159 
3160   Level: beginner
3161 
3162   Notes:
3163   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3164 
3165   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3166 
3167 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3168 @*/
3169 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3170 {
3171   DM_Plex *mesh = (DM_Plex *)dm->data;
3172   PetscInt pStart, pEnd;
3173   PetscInt dof, off, c;
3174 
3175   PetscFunctionBegin;
3176   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3177   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3178   if (dof) PetscAssertPointer(coneOrientation, 3);
3179   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3180   if (PetscDefined(USE_DEBUG)) {
3181     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3182     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3183     for (c = 0; c < dof; ++c) {
3184       PetscInt cdof, o = coneOrientation[c];
3185 
3186       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3187       PetscCheck(!o || (o >= -(cdof + 1) && o < cdof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof + 1), cdof);
3188       mesh->coneOrientations[off + c] = o;
3189     }
3190   } else {
3191     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3192   }
3193   PetscFunctionReturn(PETSC_SUCCESS);
3194 }
3195 
3196 /*@
3197   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3198 
3199   Not Collective
3200 
3201   Input Parameters:
3202 + dm        - The `DMPLEX`
3203 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3204 . conePos   - The local index in the cone where the point should be put
3205 - conePoint - The mesh point to insert
3206 
3207   Level: beginner
3208 
3209 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3210 @*/
3211 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3212 {
3213   DM_Plex *mesh = (DM_Plex *)dm->data;
3214   PetscInt pStart, pEnd;
3215   PetscInt dof, off;
3216 
3217   PetscFunctionBegin;
3218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3219   if (PetscDefined(USE_DEBUG)) {
3220     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3221     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3222     PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3223     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3224     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3225   }
3226   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3227   mesh->cones[off + conePos] = conePoint;
3228   PetscFunctionReturn(PETSC_SUCCESS);
3229 }
3230 
3231 /*@
3232   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3233 
3234   Not Collective
3235 
3236   Input Parameters:
3237 + dm              - The `DMPLEX`
3238 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3239 . conePos         - The local index in the cone where the point should be put
3240 - coneOrientation - The point orientation to insert
3241 
3242   Level: beginner
3243 
3244   Note:
3245   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3246 
3247 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3248 @*/
3249 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3250 {
3251   DM_Plex *mesh = (DM_Plex *)dm->data;
3252   PetscInt pStart, pEnd;
3253   PetscInt dof, off;
3254 
3255   PetscFunctionBegin;
3256   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3257   if (PetscDefined(USE_DEBUG)) {
3258     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3259     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3260     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3261     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3262   }
3263   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3264   mesh->coneOrientations[off + conePos] = coneOrientation;
3265   PetscFunctionReturn(PETSC_SUCCESS);
3266 }
3267 
3268 /*@C
3269   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3270 
3271   Not collective
3272 
3273   Input Parameters:
3274 + dm - The DMPlex
3275 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3276 
3277   Output Parameters:
3278 + cone - An array of points which are on the in-edges for point `p`
3279 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3280         integer giving the prescription for cone traversal.
3281 
3282   Level: beginner
3283 
3284   Notes:
3285   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3286   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3287   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3288   with the identity.
3289 
3290   Fortran Notes:
3291   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3292   `DMPlexRestoreCone()` is not needed/available in C.
3293 
3294 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3295 @*/
3296 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3297 {
3298   DM_Plex *mesh = (DM_Plex *)dm->data;
3299 
3300   PetscFunctionBegin;
3301   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3302   if (mesh->tr) {
3303     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3304   } else {
3305     PetscInt off;
3306     if (PetscDefined(USE_DEBUG)) {
3307       PetscInt dof;
3308       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3309       if (dof) {
3310         if (cone) PetscAssertPointer(cone, 3);
3311         if (ornt) PetscAssertPointer(ornt, 4);
3312       }
3313     }
3314     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3315     if (cone) *cone = mesh->cones ? mesh->cones + off : NULL; // NULL + 0 is UB
3316     if (ornt) *ornt = mesh->coneOrientations ? mesh->coneOrientations + off : NULL;
3317   }
3318   PetscFunctionReturn(PETSC_SUCCESS);
3319 }
3320 
3321 /*@C
3322   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3323 
3324   Not Collective
3325 
3326   Input Parameters:
3327 + dm   - The DMPlex
3328 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3329 . cone - An array of points which are on the in-edges for point p
3330 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3331         integer giving the prescription for cone traversal.
3332 
3333   Level: beginner
3334 
3335   Notes:
3336   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3337   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3338   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3339   with the identity.
3340 
3341   Fortran Notes:
3342   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3343   `DMPlexRestoreCone()` is not needed/available in C.
3344 
3345 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3346 @*/
3347 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3348 {
3349   DM_Plex *mesh = (DM_Plex *)dm->data;
3350 
3351   PetscFunctionBegin;
3352   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3353   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3354   PetscFunctionReturn(PETSC_SUCCESS);
3355 }
3356 
3357 /*@
3358   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3359 
3360   Not Collective
3361 
3362   Input Parameters:
3363 + dm - The `DMPLEX`
3364 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3365 
3366   Output Parameter:
3367 . size - The support size for point `p`
3368 
3369   Level: beginner
3370 
3371 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3372 @*/
3373 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3374 {
3375   DM_Plex *mesh = (DM_Plex *)dm->data;
3376 
3377   PetscFunctionBegin;
3378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3379   PetscAssertPointer(size, 3);
3380   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3381   PetscFunctionReturn(PETSC_SUCCESS);
3382 }
3383 
3384 /*@
3385   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3386 
3387   Not Collective
3388 
3389   Input Parameters:
3390 + dm   - The `DMPLEX`
3391 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3392 - size - The support size for point `p`
3393 
3394   Level: beginner
3395 
3396   Note:
3397   This should be called after `DMPlexSetChart()`.
3398 
3399 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3400 @*/
3401 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3402 {
3403   DM_Plex *mesh = (DM_Plex *)dm->data;
3404 
3405   PetscFunctionBegin;
3406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3407   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3408   PetscFunctionReturn(PETSC_SUCCESS);
3409 }
3410 
3411 /*@C
3412   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3413 
3414   Not Collective
3415 
3416   Input Parameters:
3417 + dm - The `DMPLEX`
3418 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3419 
3420   Output Parameter:
3421 . support - An array of points which are on the out-edges for point `p`
3422 
3423   Level: beginner
3424 
3425   Fortran Notes:
3426   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3427   `DMPlexRestoreSupport()` is not needed/available in C.
3428 
3429 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3430 @*/
3431 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3432 {
3433   DM_Plex *mesh = (DM_Plex *)dm->data;
3434   PetscInt off;
3435 
3436   PetscFunctionBegin;
3437   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3438   PetscAssertPointer(support, 3);
3439   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3440   *support = mesh->supports ? mesh->supports + off : NULL; //NULL + 0 is UB
3441   PetscFunctionReturn(PETSC_SUCCESS);
3442 }
3443 
3444 /*@
3445   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3446 
3447   Not Collective
3448 
3449   Input Parameters:
3450 + dm      - The `DMPLEX`
3451 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3452 - support - An array of points which are on the out-edges for point `p`
3453 
3454   Level: beginner
3455 
3456   Note:
3457   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3458 
3459 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3460 @*/
3461 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3462 {
3463   DM_Plex *mesh = (DM_Plex *)dm->data;
3464   PetscInt pStart, pEnd;
3465   PetscInt dof, off, c;
3466 
3467   PetscFunctionBegin;
3468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3469   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3470   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3471   if (dof) PetscAssertPointer(support, 3);
3472   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3473   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3474   for (c = 0; c < dof; ++c) {
3475     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3476     mesh->supports[off + c] = support[c];
3477   }
3478   PetscFunctionReturn(PETSC_SUCCESS);
3479 }
3480 
3481 /*@
3482   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3483 
3484   Not Collective
3485 
3486   Input Parameters:
3487 + dm           - The `DMPLEX`
3488 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3489 . supportPos   - The local index in the cone where the point should be put
3490 - supportPoint - The mesh point to insert
3491 
3492   Level: beginner
3493 
3494 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3495 @*/
3496 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3497 {
3498   DM_Plex *mesh = (DM_Plex *)dm->data;
3499   PetscInt pStart, pEnd;
3500   PetscInt dof, off;
3501 
3502   PetscFunctionBegin;
3503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3504   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3505   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3506   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3507   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3508   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3509   PetscCheck(supportPos < dof, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3510   mesh->supports[off + supportPos] = supportPoint;
3511   PetscFunctionReturn(PETSC_SUCCESS);
3512 }
3513 
3514 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3515 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3516 {
3517   switch (ct) {
3518   case DM_POLYTOPE_SEGMENT:
3519     if (o == -1) return -2;
3520     break;
3521   case DM_POLYTOPE_TRIANGLE:
3522     if (o == -3) return -1;
3523     if (o == -2) return -3;
3524     if (o == -1) return -2;
3525     break;
3526   case DM_POLYTOPE_QUADRILATERAL:
3527     if (o == -4) return -2;
3528     if (o == -3) return -1;
3529     if (o == -2) return -4;
3530     if (o == -1) return -3;
3531     break;
3532   default:
3533     return o;
3534   }
3535   return o;
3536 }
3537 
3538 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3539 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3540 {
3541   switch (ct) {
3542   case DM_POLYTOPE_SEGMENT:
3543     if ((o == -2) || (o == 1)) return -1;
3544     if (o == -1) return 0;
3545     break;
3546   case DM_POLYTOPE_TRIANGLE:
3547     if (o == -3) return -2;
3548     if (o == -2) return -1;
3549     if (o == -1) return -3;
3550     break;
3551   case DM_POLYTOPE_QUADRILATERAL:
3552     if (o == -4) return -2;
3553     if (o == -3) return -1;
3554     if (o == -2) return -4;
3555     if (o == -1) return -3;
3556     break;
3557   default:
3558     return o;
3559   }
3560   return o;
3561 }
3562 
3563 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3564 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3565 {
3566   PetscInt pStart, pEnd, p;
3567 
3568   PetscFunctionBegin;
3569   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3570   for (p = pStart; p < pEnd; ++p) {
3571     const PetscInt *cone, *ornt;
3572     PetscInt        coneSize, c;
3573 
3574     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3575     PetscCall(DMPlexGetCone(dm, p, &cone));
3576     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3577     for (c = 0; c < coneSize; ++c) {
3578       DMPolytopeType ct;
3579       const PetscInt o = ornt[c];
3580 
3581       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3582       switch (ct) {
3583       case DM_POLYTOPE_SEGMENT:
3584         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3585         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3586         break;
3587       case DM_POLYTOPE_TRIANGLE:
3588         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3589         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3590         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3591         break;
3592       case DM_POLYTOPE_QUADRILATERAL:
3593         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3594         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3595         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3596         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3597         break;
3598       default:
3599         break;
3600       }
3601     }
3602   }
3603   PetscFunctionReturn(PETSC_SUCCESS);
3604 }
3605 
3606 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3607 {
3608   DM_Plex *mesh = (DM_Plex *)dm->data;
3609 
3610   PetscFunctionBeginHot;
3611   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3612     if (useCone) {
3613       PetscCall(DMPlexGetConeSize(dm, p, size));
3614       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3615     } else {
3616       PetscCall(DMPlexGetSupportSize(dm, p, size));
3617       PetscCall(DMPlexGetSupport(dm, p, arr));
3618     }
3619   } else {
3620     if (useCone) {
3621       const PetscSection s   = mesh->coneSection;
3622       const PetscInt     ps  = p - s->pStart;
3623       const PetscInt     off = s->atlasOff[ps];
3624 
3625       *size = s->atlasDof[ps];
3626       *arr  = mesh->cones + off;
3627       *ornt = mesh->coneOrientations + off;
3628     } else {
3629       const PetscSection s   = mesh->supportSection;
3630       const PetscInt     ps  = p - s->pStart;
3631       const PetscInt     off = s->atlasOff[ps];
3632 
3633       *size = s->atlasDof[ps];
3634       *arr  = mesh->supports + off;
3635     }
3636   }
3637   PetscFunctionReturn(PETSC_SUCCESS);
3638 }
3639 
3640 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3641 {
3642   DM_Plex *mesh = (DM_Plex *)dm->data;
3643 
3644   PetscFunctionBeginHot;
3645   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3646     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3647   }
3648   PetscFunctionReturn(PETSC_SUCCESS);
3649 }
3650 
3651 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3652 {
3653   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3654   PetscInt       *closure;
3655   const PetscInt *tmp = NULL, *tmpO = NULL;
3656   PetscInt        off = 0, tmpSize, t;
3657 
3658   PetscFunctionBeginHot;
3659   if (ornt) {
3660     PetscCall(DMPlexGetCellType(dm, p, &ct));
3661     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3662   }
3663   if (*points) {
3664     closure = *points;
3665   } else {
3666     PetscInt maxConeSize, maxSupportSize;
3667     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3668     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3669   }
3670   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3671   if (ct == DM_POLYTOPE_UNKNOWN) {
3672     closure[off++] = p;
3673     closure[off++] = 0;
3674     for (t = 0; t < tmpSize; ++t) {
3675       closure[off++] = tmp[t];
3676       closure[off++] = tmpO ? tmpO[t] : 0;
3677     }
3678   } else {
3679     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3680 
3681     /* We assume that cells with a valid type have faces with a valid type */
3682     closure[off++] = p;
3683     closure[off++] = ornt;
3684     for (t = 0; t < tmpSize; ++t) {
3685       DMPolytopeType ft;
3686 
3687       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3688       closure[off++] = tmp[arr[t]];
3689       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3690     }
3691   }
3692   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3693   if (numPoints) *numPoints = tmpSize + 1;
3694   if (points) *points = closure;
3695   PetscFunctionReturn(PETSC_SUCCESS);
3696 }
3697 
3698 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3699 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3700 {
3701   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3702   const PetscInt *cone, *ornt;
3703   PetscInt       *pts, *closure = NULL;
3704   DMPolytopeType  ft;
3705   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3706   PetscInt        dim, coneSize, c, d, clSize, cl;
3707 
3708   PetscFunctionBeginHot;
3709   PetscCall(DMGetDimension(dm, &dim));
3710   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3711   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3712   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3713   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3714   maxSize       = PetscMax(coneSeries, supportSeries);
3715   if (*points) {
3716     pts = *points;
3717   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3718   c        = 0;
3719   pts[c++] = point;
3720   pts[c++] = o;
3721   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3722   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3723   for (cl = 0; cl < clSize * 2; cl += 2) {
3724     pts[c++] = closure[cl];
3725     pts[c++] = closure[cl + 1];
3726   }
3727   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3728   for (cl = 0; cl < clSize * 2; cl += 2) {
3729     pts[c++] = closure[cl];
3730     pts[c++] = closure[cl + 1];
3731   }
3732   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3733   for (d = 2; d < coneSize; ++d) {
3734     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3735     pts[c++] = cone[arr[d * 2 + 0]];
3736     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3737   }
3738   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3739   if (dim >= 3) {
3740     for (d = 2; d < coneSize; ++d) {
3741       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3742       const PetscInt *fcone, *fornt;
3743       PetscInt        fconeSize, fc, i;
3744 
3745       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3746       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3747       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3748       for (fc = 0; fc < fconeSize; ++fc) {
3749         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3750         const PetscInt co = farr[fc * 2 + 1];
3751 
3752         for (i = 0; i < c; i += 2)
3753           if (pts[i] == cp) break;
3754         if (i == c) {
3755           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3756           pts[c++] = cp;
3757           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3758         }
3759       }
3760       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3761     }
3762   }
3763   *numPoints = c / 2;
3764   *points    = pts;
3765   PetscFunctionReturn(PETSC_SUCCESS);
3766 }
3767 
3768 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3769 {
3770   DMPolytopeType ct;
3771   PetscInt      *closure, *fifo;
3772   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3773   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3774   PetscInt       depth, maxSize;
3775 
3776   PetscFunctionBeginHot;
3777   PetscCall(DMPlexGetDepth(dm, &depth));
3778   if (depth == 1) {
3779     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3780     PetscFunctionReturn(PETSC_SUCCESS);
3781   }
3782   PetscCall(DMPlexGetCellType(dm, p, &ct));
3783   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3784   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3785     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3786     PetscFunctionReturn(PETSC_SUCCESS);
3787   }
3788   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3789   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3790   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3791   maxSize       = PetscMax(coneSeries, supportSeries);
3792   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3793   if (*points) {
3794     closure = *points;
3795   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3796   closure[closureSize++] = p;
3797   closure[closureSize++] = ornt;
3798   fifo[fifoSize++]       = p;
3799   fifo[fifoSize++]       = ornt;
3800   fifo[fifoSize++]       = ct;
3801   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3802   while (fifoSize - fifoStart) {
3803     const PetscInt       q    = fifo[fifoStart++];
3804     const PetscInt       o    = fifo[fifoStart++];
3805     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3806     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3807     const PetscInt      *tmp, *tmpO = NULL;
3808     PetscInt             tmpSize, t;
3809 
3810     if (PetscDefined(USE_DEBUG)) {
3811       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3812       PetscCheck(!o || !(o >= nO || o < -nO), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
3813     }
3814     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3815     for (t = 0; t < tmpSize; ++t) {
3816       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3817       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3818       const PetscInt cp = tmp[ip];
3819       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3820       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3821       PetscInt       c;
3822 
3823       /* Check for duplicate */
3824       for (c = 0; c < closureSize; c += 2) {
3825         if (closure[c] == cp) break;
3826       }
3827       if (c == closureSize) {
3828         closure[closureSize++] = cp;
3829         closure[closureSize++] = co;
3830         fifo[fifoSize++]       = cp;
3831         fifo[fifoSize++]       = co;
3832         fifo[fifoSize++]       = ct;
3833       }
3834     }
3835     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3836   }
3837   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3838   if (numPoints) *numPoints = closureSize / 2;
3839   if (points) *points = closure;
3840   PetscFunctionReturn(PETSC_SUCCESS);
3841 }
3842 
3843 /*@C
3844   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3845 
3846   Not Collective
3847 
3848   Input Parameters:
3849 + dm      - The `DMPLEX`
3850 . p       - The mesh point
3851 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3852 
3853   Input/Output Parameter:
3854 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3855            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
3856 
3857   Output Parameter:
3858 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3859 
3860   Level: beginner
3861 
3862   Note:
3863   If using internal storage (points is `NULL` on input), each call overwrites the last output.
3864 
3865   Fortran Notes:
3866   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
3867 
3868 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3869 @*/
3870 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3871 {
3872   PetscFunctionBeginHot;
3873   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3874   if (numPoints) PetscAssertPointer(numPoints, 4);
3875   if (points) PetscAssertPointer(points, 5);
3876   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3877   PetscFunctionReturn(PETSC_SUCCESS);
3878 }
3879 
3880 /*@C
3881   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3882 
3883   Not Collective
3884 
3885   Input Parameters:
3886 + dm        - The `DMPLEX`
3887 . p         - The mesh point
3888 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3889 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3890 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3891 
3892   Level: beginner
3893 
3894   Note:
3895   If not using internal storage (points is not `NULL` on input), this call is unnecessary
3896 
3897 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3898 @*/
3899 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3900 {
3901   PetscFunctionBeginHot;
3902   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3903   if (numPoints) *numPoints = 0;
3904   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3905   PetscFunctionReturn(PETSC_SUCCESS);
3906 }
3907 
3908 /*@
3909   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3910 
3911   Not Collective
3912 
3913   Input Parameter:
3914 . dm - The `DMPLEX`
3915 
3916   Output Parameters:
3917 + maxConeSize    - The maximum number of in-edges
3918 - maxSupportSize - The maximum number of out-edges
3919 
3920   Level: beginner
3921 
3922 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3923 @*/
3924 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3925 {
3926   DM_Plex *mesh = (DM_Plex *)dm->data;
3927 
3928   PetscFunctionBegin;
3929   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3930   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3931   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3932   PetscFunctionReturn(PETSC_SUCCESS);
3933 }
3934 
3935 PetscErrorCode DMSetUp_Plex(DM dm)
3936 {
3937   DM_Plex *mesh = (DM_Plex *)dm->data;
3938   PetscInt size, maxSupportSize;
3939 
3940   PetscFunctionBegin;
3941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3942   PetscCall(PetscSectionSetUp(mesh->coneSection));
3943   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3944   PetscCall(PetscMalloc1(size, &mesh->cones));
3945   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3946   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3947   if (maxSupportSize) {
3948     PetscCall(PetscSectionSetUp(mesh->supportSection));
3949     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3950     PetscCall(PetscMalloc1(size, &mesh->supports));
3951   }
3952   PetscFunctionReturn(PETSC_SUCCESS);
3953 }
3954 
3955 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3956 {
3957   PetscFunctionBegin;
3958   if (subdm) PetscCall(DMClone(dm, subdm));
3959   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3960   if (subdm) (*subdm)->useNatural = dm->useNatural;
3961   if (dm->useNatural && dm->sfMigration) {
3962     PetscSF sfNatural;
3963 
3964     (*subdm)->sfMigration = dm->sfMigration;
3965     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3966     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3967     (*subdm)->sfNatural = sfNatural;
3968   }
3969   PetscFunctionReturn(PETSC_SUCCESS);
3970 }
3971 
3972 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3973 {
3974   PetscInt i = 0;
3975 
3976   PetscFunctionBegin;
3977   PetscCall(DMClone(dms[0], superdm));
3978   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3979   (*superdm)->useNatural = PETSC_FALSE;
3980   for (i = 0; i < len; i++) {
3981     if (dms[i]->useNatural && dms[i]->sfMigration) {
3982       PetscSF sfNatural;
3983 
3984       (*superdm)->sfMigration = dms[i]->sfMigration;
3985       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3986       (*superdm)->useNatural = PETSC_TRUE;
3987       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3988       (*superdm)->sfNatural = sfNatural;
3989       break;
3990     }
3991   }
3992   PetscFunctionReturn(PETSC_SUCCESS);
3993 }
3994 
3995 /*@
3996   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3997 
3998   Not Collective
3999 
4000   Input Parameter:
4001 . dm - The `DMPLEX`
4002 
4003   Level: beginner
4004 
4005   Note:
4006   This should be called after all calls to `DMPlexSetCone()`
4007 
4008 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4009 @*/
4010 PetscErrorCode DMPlexSymmetrize(DM dm)
4011 {
4012   DM_Plex  *mesh = (DM_Plex *)dm->data;
4013   PetscInt *offsets;
4014   PetscInt  supportSize;
4015   PetscInt  pStart, pEnd, p;
4016 
4017   PetscFunctionBegin;
4018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4019   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4020   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4021   /* Calculate support sizes */
4022   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4023   for (p = pStart; p < pEnd; ++p) {
4024     PetscInt dof, off, c;
4025 
4026     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4027     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4028     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4029   }
4030   PetscCall(PetscSectionSetUp(mesh->supportSection));
4031   /* Calculate supports */
4032   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4033   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4034   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4035   for (p = pStart; p < pEnd; ++p) {
4036     PetscInt dof, off, c;
4037 
4038     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4039     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4040     for (c = off; c < off + dof; ++c) {
4041       const PetscInt q = mesh->cones[c];
4042       PetscInt       offS;
4043 
4044       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4045 
4046       mesh->supports[offS + offsets[q]] = p;
4047       ++offsets[q];
4048     }
4049   }
4050   PetscCall(PetscFree(offsets));
4051   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4052   PetscFunctionReturn(PETSC_SUCCESS);
4053 }
4054 
4055 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4056 {
4057   IS stratumIS;
4058 
4059   PetscFunctionBegin;
4060   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4061   if (PetscDefined(USE_DEBUG)) {
4062     PetscInt  qStart, qEnd, numLevels, level;
4063     PetscBool overlap = PETSC_FALSE;
4064     PetscCall(DMLabelGetNumValues(label, &numLevels));
4065     for (level = 0; level < numLevels; level++) {
4066       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4067       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4068         overlap = PETSC_TRUE;
4069         break;
4070       }
4071     }
4072     PetscCheck(!overlap, PETSC_COMM_SELF, PETSC_ERR_PLIB, "New depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ") overlaps with depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ")", depth, pStart, pEnd, level, qStart, qEnd);
4073   }
4074   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4075   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4076   PetscCall(ISDestroy(&stratumIS));
4077   PetscFunctionReturn(PETSC_SUCCESS);
4078 }
4079 
4080 /*@
4081   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4082 
4083   Collective
4084 
4085   Input Parameter:
4086 . dm - The `DMPLEX`
4087 
4088   Level: beginner
4089 
4090   Notes:
4091   The strata group all points of the same grade, and this function calculates the strata. This
4092   grade can be seen as the height (or depth) of the point in the DAG.
4093 
4094   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4095   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
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 . dm - 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 Notes:
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   PetscAssertPointer(points, 3);
4419   PetscAssertPointer(numCoveredPoints, 4);
4420   PetscAssertPointer(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 Notes:
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) PetscAssertPointer(points, 3);
4479   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4480   PetscAssertPointer(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 Notes:
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   PetscAssertPointer(points, 3);
4517   PetscAssertPointer(numCoveredPoints, 4);
4518   PetscAssertPointer(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 + numCoveringPoints - The number of points in the meet
4595 - coveringPoints    - 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   PetscAssertPointer(points, 3);
4618   PetscAssertPointer(numCoveringPoints, 4);
4619   PetscAssertPointer(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 Notes:
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) PetscAssertPointer(points, 3);
4678   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4679   PetscAssertPointer(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 Notes:
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   PetscAssertPointer(points, 3);
4716   PetscAssertPointer(numCoveredPoints, 4);
4717   PetscAssertPointer(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   PetscAssertPointer(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   PetscAssertPointer(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   PetscAssertPointer(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   PetscAssertPointer(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     PetscAssertPointer(start, 3);
5030     *start = 0;
5031   }
5032   if (end) {
5033     PetscAssertPointer(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     PetscAssertPointer(start, 3);
5084     *start = 0;
5085   }
5086   if (end) {
5087     PetscAssertPointer(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   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5099   else PetscCall(DMGetDimension(dm, &depth));
5100   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5101   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5102   PetscFunctionReturn(PETSC_SUCCESS);
5103 }
5104 
5105 /*@
5106   DMPlexGetPointDepth - Get the `depth` of a given point
5107 
5108   Not Collective
5109 
5110   Input Parameters:
5111 + dm    - The `DMPLEX` object
5112 - point - The point
5113 
5114   Output Parameter:
5115 . depth - The depth of the `point`
5116 
5117   Level: intermediate
5118 
5119 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5120 @*/
5121 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5122 {
5123   PetscFunctionBegin;
5124   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5125   PetscAssertPointer(depth, 3);
5126   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5127   PetscFunctionReturn(PETSC_SUCCESS);
5128 }
5129 
5130 /*@
5131   DMPlexGetPointHeight - Get the `height` of a given point
5132 
5133   Not Collective
5134 
5135   Input Parameters:
5136 + dm    - The `DMPLEX` object
5137 - point - The point
5138 
5139   Output Parameter:
5140 . height - The height of the `point`
5141 
5142   Level: intermediate
5143 
5144 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5145 @*/
5146 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5147 {
5148   PetscInt n, pDepth;
5149 
5150   PetscFunctionBegin;
5151   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5152   PetscAssertPointer(height, 3);
5153   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5154   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5155   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5156   PetscFunctionReturn(PETSC_SUCCESS);
5157 }
5158 
5159 /*@
5160   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5161 
5162   Not Collective
5163 
5164   Input Parameter:
5165 . dm - The `DMPLEX` object
5166 
5167   Output Parameter:
5168 . celltypeLabel - The `DMLabel` recording cell polytope type
5169 
5170   Level: developer
5171 
5172   Note:
5173   This function will trigger automatica computation of cell types. This can be disabled by calling
5174   `DMCreateLabel`(dm, "celltype") beforehand.
5175 
5176 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5177 @*/
5178 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5179 {
5180   PetscFunctionBegin;
5181   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5182   PetscAssertPointer(celltypeLabel, 2);
5183   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5184   *celltypeLabel = dm->celltypeLabel;
5185   PetscFunctionReturn(PETSC_SUCCESS);
5186 }
5187 
5188 /*@
5189   DMPlexGetCellType - Get the polytope type of a given cell
5190 
5191   Not Collective
5192 
5193   Input Parameters:
5194 + dm   - The `DMPLEX` object
5195 - cell - The cell
5196 
5197   Output Parameter:
5198 . celltype - The polytope type of the cell
5199 
5200   Level: intermediate
5201 
5202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5203 @*/
5204 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5205 {
5206   DM_Plex *mesh = (DM_Plex *)dm->data;
5207   DMLabel  label;
5208   PetscInt ct;
5209 
5210   PetscFunctionBegin;
5211   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5212   PetscAssertPointer(celltype, 3);
5213   if (mesh->tr) {
5214     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5215   } else {
5216     PetscInt pStart, pEnd;
5217 
5218     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5219     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5220       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5221       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5222       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5223       for (PetscInt p = pStart; p < pEnd; p++) {
5224         PetscCall(DMLabelGetValue(label, p, &ct));
5225         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5226       }
5227     }
5228     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5229     if (PetscDefined(USE_DEBUG)) {
5230       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5231       PetscCall(DMLabelGetValue(label, cell, &ct));
5232       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5233       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5234     }
5235   }
5236   PetscFunctionReturn(PETSC_SUCCESS);
5237 }
5238 
5239 /*@
5240   DMPlexSetCellType - Set the polytope type of a given cell
5241 
5242   Not Collective
5243 
5244   Input Parameters:
5245 + dm       - The `DMPLEX` object
5246 . cell     - The cell
5247 - celltype - The polytope type of the cell
5248 
5249   Level: advanced
5250 
5251   Note:
5252   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5253   is executed. This function will override the computed type. However, if automatic classification will not succeed
5254   and a user wants to manually specify all types, the classification must be disabled by calling
5255   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5256 
5257 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5258 @*/
5259 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5260 {
5261   DM_Plex *mesh = (DM_Plex *)dm->data;
5262   DMLabel  label;
5263   PetscInt pStart, pEnd;
5264 
5265   PetscFunctionBegin;
5266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5267   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5268   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5269   PetscCall(DMLabelSetValue(label, cell, celltype));
5270   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5271   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5272   PetscFunctionReturn(PETSC_SUCCESS);
5273 }
5274 
5275 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5276 {
5277   PetscSection section, s;
5278   Mat          m;
5279   PetscInt     maxHeight;
5280   const char  *prefix;
5281 
5282   PetscFunctionBegin;
5283   PetscCall(DMClone(dm, cdm));
5284   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5285   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5286   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5287   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5288   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5289   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5290   PetscCall(DMSetLocalSection(*cdm, section));
5291   PetscCall(PetscSectionDestroy(&section));
5292   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5293   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5294   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5295   PetscCall(PetscSectionDestroy(&s));
5296   PetscCall(MatDestroy(&m));
5297 
5298   PetscCall(DMSetNumFields(*cdm, 1));
5299   PetscCall(DMCreateDS(*cdm));
5300   (*cdm)->cloneOpts = PETSC_TRUE;
5301   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5302   PetscFunctionReturn(PETSC_SUCCESS);
5303 }
5304 
5305 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5306 {
5307   Vec coordsLocal, cellCoordsLocal;
5308   DM  coordsDM, cellCoordsDM;
5309 
5310   PetscFunctionBegin;
5311   *field = NULL;
5312   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5313   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5314   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5315   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5316   if (coordsLocal && coordsDM) {
5317     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5318     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5319   }
5320   PetscFunctionReturn(PETSC_SUCCESS);
5321 }
5322 
5323 /*@C
5324   DMPlexGetConeSection - Return a section which describes the layout of cone data
5325 
5326   Not Collective
5327 
5328   Input Parameter:
5329 . dm - The `DMPLEX` object
5330 
5331   Output Parameter:
5332 . section - The `PetscSection` object
5333 
5334   Level: developer
5335 
5336 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5337 @*/
5338 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5339 {
5340   DM_Plex *mesh = (DM_Plex *)dm->data;
5341 
5342   PetscFunctionBegin;
5343   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5344   if (section) *section = mesh->coneSection;
5345   PetscFunctionReturn(PETSC_SUCCESS);
5346 }
5347 
5348 /*@C
5349   DMPlexGetSupportSection - Return a section which describes the layout of support data
5350 
5351   Not Collective
5352 
5353   Input Parameter:
5354 . dm - The `DMPLEX` object
5355 
5356   Output Parameter:
5357 . section - The `PetscSection` object
5358 
5359   Level: developer
5360 
5361 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5362 @*/
5363 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5364 {
5365   DM_Plex *mesh = (DM_Plex *)dm->data;
5366 
5367   PetscFunctionBegin;
5368   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5369   if (section) *section = mesh->supportSection;
5370   PetscFunctionReturn(PETSC_SUCCESS);
5371 }
5372 
5373 /*@C
5374   DMPlexGetCones - Return cone data
5375 
5376   Not Collective
5377 
5378   Input Parameter:
5379 . dm - The `DMPLEX` object
5380 
5381   Output Parameter:
5382 . cones - The cone for each point
5383 
5384   Level: developer
5385 
5386 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5387 @*/
5388 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5389 {
5390   DM_Plex *mesh = (DM_Plex *)dm->data;
5391 
5392   PetscFunctionBegin;
5393   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5394   if (cones) *cones = mesh->cones;
5395   PetscFunctionReturn(PETSC_SUCCESS);
5396 }
5397 
5398 /*@C
5399   DMPlexGetConeOrientations - Return cone orientation data
5400 
5401   Not Collective
5402 
5403   Input Parameter:
5404 . dm - The `DMPLEX` object
5405 
5406   Output Parameter:
5407 . coneOrientations - The array of cone orientations for all points
5408 
5409   Level: developer
5410 
5411   Notes:
5412   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5413 
5414   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5415 
5416 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5417 @*/
5418 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5419 {
5420   DM_Plex *mesh = (DM_Plex *)dm->data;
5421 
5422   PetscFunctionBegin;
5423   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5424   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5425   PetscFunctionReturn(PETSC_SUCCESS);
5426 }
5427 
5428 /******************************** FEM Support **********************************/
5429 
5430 /*
5431  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5432  representing a line in the section.
5433 */
5434 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5435 {
5436   PetscFunctionBeginHot;
5437   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5438   if (line < 0) {
5439     *k  = 0;
5440     *Nc = 0;
5441   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5442     *k = 1;
5443   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5444     /* An order k SEM disc has k-1 dofs on an edge */
5445     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5446     *k = *k / *Nc + 1;
5447   }
5448   PetscFunctionReturn(PETSC_SUCCESS);
5449 }
5450 
5451 /*@
5452 
5453   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5454   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5455   section provided (or the section of the `DM`).
5456 
5457   Input Parameters:
5458 + dm      - The `DM`
5459 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5460 - section - The `PetscSection` to reorder, or `NULL` for the default section
5461 
5462   Example:
5463   A typical interpolated single-quad mesh might order points as
5464 .vb
5465   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5466 
5467   v4 -- e6 -- v3
5468   |           |
5469   e7    c0    e8
5470   |           |
5471   v1 -- e5 -- v2
5472 .ve
5473 
5474   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5475   dofs in the order of points, e.g.,
5476 .vb
5477     c0 -> [0,1,2,3]
5478     v1 -> [4]
5479     ...
5480     e5 -> [8, 9]
5481 .ve
5482 
5483   which corresponds to the dofs
5484 .vb
5485     6   10  11  7
5486     13  2   3   15
5487     12  0   1   14
5488     4   8   9   5
5489 .ve
5490 
5491   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5492 .vb
5493   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5494 .ve
5495 
5496   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5497 .vb
5498    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5499 .ve
5500 
5501   Level: developer
5502 
5503   Notes:
5504   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5505   degree of the basis.
5506 
5507   This is required to run with libCEED.
5508 
5509 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5510 @*/
5511 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5512 {
5513   DMLabel   label;
5514   PetscInt  dim, depth = -1, eStart = -1, Nf;
5515   PetscBool vertexchart;
5516 
5517   PetscFunctionBegin;
5518   PetscCall(DMGetDimension(dm, &dim));
5519   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5520   if (point < 0) {
5521     PetscInt sStart, sEnd;
5522 
5523     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5524     point = sEnd - sStart ? sStart : point;
5525   }
5526   PetscCall(DMPlexGetDepthLabel(dm, &label));
5527   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5528   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5529   if (depth == 1) {
5530     eStart = point;
5531   } else if (depth == dim) {
5532     const PetscInt *cone;
5533 
5534     PetscCall(DMPlexGetCone(dm, point, &cone));
5535     if (dim == 2) eStart = cone[0];
5536     else if (dim == 3) {
5537       const PetscInt *cone2;
5538       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5539       eStart = cone2[0];
5540     } 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);
5541   } 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);
5542   { /* Determine whether the chart covers all points or just vertices. */
5543     PetscInt pStart, pEnd, cStart, cEnd;
5544     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5545     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5546     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5547     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5548     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5549   }
5550   PetscCall(PetscSectionGetNumFields(section, &Nf));
5551   for (PetscInt d = 1; d <= dim; d++) {
5552     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5553     PetscInt *perm;
5554 
5555     for (f = 0; f < Nf; ++f) {
5556       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5557       size += PetscPowInt(k + 1, d) * Nc;
5558     }
5559     PetscCall(PetscMalloc1(size, &perm));
5560     for (f = 0; f < Nf; ++f) {
5561       switch (d) {
5562       case 1:
5563         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5564         /*
5565          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5566          We want              [ vtx0; edge of length k-1; vtx1 ]
5567          */
5568         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5569         for (i = 0; i < k - 1; i++)
5570           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5571         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5572         foffset = offset;
5573         break;
5574       case 2:
5575         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5576         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5577         /* The SEM order is
5578 
5579          v_lb, {e_b}, v_rb,
5580          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5581          v_lt, reverse {e_t}, v_rt
5582          */
5583         {
5584           const PetscInt of   = 0;
5585           const PetscInt oeb  = of + PetscSqr(k - 1);
5586           const PetscInt oer  = oeb + (k - 1);
5587           const PetscInt oet  = oer + (k - 1);
5588           const PetscInt oel  = oet + (k - 1);
5589           const PetscInt ovlb = oel + (k - 1);
5590           const PetscInt ovrb = ovlb + 1;
5591           const PetscInt ovrt = ovrb + 1;
5592           const PetscInt ovlt = ovrt + 1;
5593           PetscInt       o;
5594 
5595           /* bottom */
5596           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5597           for (o = oeb; o < oer; ++o)
5598             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5599           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5600           /* middle */
5601           for (i = 0; i < k - 1; ++i) {
5602             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5603             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5604               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5605             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5606           }
5607           /* top */
5608           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5609           for (o = oel - 1; o >= oet; --o)
5610             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5611           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5612           foffset = offset;
5613         }
5614         break;
5615       case 3:
5616         /* The original hex closure is
5617 
5618          {c,
5619          f_b, f_t, f_f, f_b, f_r, f_l,
5620          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5621          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5622          */
5623         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5624         /* The SEM order is
5625          Bottom Slice
5626          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5627          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5628          v_blb, {e_bb}, v_brb,
5629 
5630          Middle Slice (j)
5631          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5632          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5633          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5634 
5635          Top Slice
5636          v_tlf, {e_tf}, v_trf,
5637          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5638          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5639          */
5640         {
5641           const PetscInt oc    = 0;
5642           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5643           const PetscInt oft   = ofb + PetscSqr(k - 1);
5644           const PetscInt off   = oft + PetscSqr(k - 1);
5645           const PetscInt ofk   = off + PetscSqr(k - 1);
5646           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5647           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5648           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5649           const PetscInt oebb  = oebl + (k - 1);
5650           const PetscInt oebr  = oebb + (k - 1);
5651           const PetscInt oebf  = oebr + (k - 1);
5652           const PetscInt oetf  = oebf + (k - 1);
5653           const PetscInt oetr  = oetf + (k - 1);
5654           const PetscInt oetb  = oetr + (k - 1);
5655           const PetscInt oetl  = oetb + (k - 1);
5656           const PetscInt oerf  = oetl + (k - 1);
5657           const PetscInt oelf  = oerf + (k - 1);
5658           const PetscInt oelb  = oelf + (k - 1);
5659           const PetscInt oerb  = oelb + (k - 1);
5660           const PetscInt ovblf = oerb + (k - 1);
5661           const PetscInt ovblb = ovblf + 1;
5662           const PetscInt ovbrb = ovblb + 1;
5663           const PetscInt ovbrf = ovbrb + 1;
5664           const PetscInt ovtlf = ovbrf + 1;
5665           const PetscInt ovtrf = ovtlf + 1;
5666           const PetscInt ovtrb = ovtrf + 1;
5667           const PetscInt ovtlb = ovtrb + 1;
5668           PetscInt       o, n;
5669 
5670           /* Bottom Slice */
5671           /*   bottom */
5672           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5673           for (o = oetf - 1; o >= oebf; --o)
5674             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5675           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5676           /*   middle */
5677           for (i = 0; i < k - 1; ++i) {
5678             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5679             for (n = 0; n < k - 1; ++n) {
5680               o = ofb + n * (k - 1) + i;
5681               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5682             }
5683             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5684           }
5685           /*   top */
5686           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5687           for (o = oebb; o < oebr; ++o)
5688             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5689           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5690 
5691           /* Middle Slice */
5692           for (j = 0; j < k - 1; ++j) {
5693             /*   bottom */
5694             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5695             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5696               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5697             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5698             /*   middle */
5699             for (i = 0; i < k - 1; ++i) {
5700               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5701               for (n = 0; n < k - 1; ++n)
5702                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5703               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5704             }
5705             /*   top */
5706             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5707             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5708               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5709             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5710           }
5711 
5712           /* Top Slice */
5713           /*   bottom */
5714           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5715           for (o = oetf; o < oetr; ++o)
5716             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5717           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5718           /*   middle */
5719           for (i = 0; i < k - 1; ++i) {
5720             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5721             for (n = 0; n < k - 1; ++n)
5722               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5723             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5724           }
5725           /*   top */
5726           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5727           for (o = oetl - 1; o >= oetb; --o)
5728             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5729           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5730 
5731           foffset = offset;
5732         }
5733         break;
5734       default:
5735         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5736       }
5737     }
5738     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5739     /* Check permutation */
5740     {
5741       PetscInt *check;
5742 
5743       PetscCall(PetscMalloc1(size, &check));
5744       for (i = 0; i < size; ++i) {
5745         check[i] = -1;
5746         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5747       }
5748       for (i = 0; i < size; ++i) check[perm[i]] = i;
5749       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5750       PetscCall(PetscFree(check));
5751     }
5752     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5753     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5754       PetscInt *loc_perm;
5755       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5756       for (PetscInt i = 0; i < size; i++) {
5757         loc_perm[i]        = perm[i];
5758         loc_perm[size + i] = size + perm[i];
5759       }
5760       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5761     }
5762   }
5763   PetscFunctionReturn(PETSC_SUCCESS);
5764 }
5765 
5766 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5767 {
5768   PetscDS  prob;
5769   PetscInt depth, Nf, h;
5770   DMLabel  label;
5771 
5772   PetscFunctionBeginHot;
5773   PetscCall(DMGetDS(dm, &prob));
5774   Nf      = prob->Nf;
5775   label   = dm->depthLabel;
5776   *dspace = NULL;
5777   if (field < Nf) {
5778     PetscObject disc = prob->disc[field];
5779 
5780     if (disc->classid == PETSCFE_CLASSID) {
5781       PetscDualSpace dsp;
5782 
5783       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5784       PetscCall(DMLabelGetNumValues(label, &depth));
5785       PetscCall(DMLabelGetValue(label, point, &h));
5786       h = depth - 1 - h;
5787       if (h) {
5788         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5789       } else {
5790         *dspace = dsp;
5791       }
5792     }
5793   }
5794   PetscFunctionReturn(PETSC_SUCCESS);
5795 }
5796 
5797 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5798 {
5799   PetscScalar       *array;
5800   const PetscScalar *vArray;
5801   const PetscInt    *cone, *coneO;
5802   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5803 
5804   PetscFunctionBeginHot;
5805   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5806   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5807   PetscCall(DMPlexGetCone(dm, point, &cone));
5808   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5809   if (!values || !*values) {
5810     if ((point >= pStart) && (point < pEnd)) {
5811       PetscInt dof;
5812 
5813       PetscCall(PetscSectionGetDof(section, point, &dof));
5814       size += dof;
5815     }
5816     for (p = 0; p < numPoints; ++p) {
5817       const PetscInt cp = cone[p];
5818       PetscInt       dof;
5819 
5820       if ((cp < pStart) || (cp >= pEnd)) continue;
5821       PetscCall(PetscSectionGetDof(section, cp, &dof));
5822       size += dof;
5823     }
5824     if (!values) {
5825       if (csize) *csize = size;
5826       PetscFunctionReturn(PETSC_SUCCESS);
5827     }
5828     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5829   } else {
5830     array = *values;
5831   }
5832   size = 0;
5833   PetscCall(VecGetArrayRead(v, &vArray));
5834   if ((point >= pStart) && (point < pEnd)) {
5835     PetscInt           dof, off, d;
5836     const PetscScalar *varr;
5837 
5838     PetscCall(PetscSectionGetDof(section, point, &dof));
5839     PetscCall(PetscSectionGetOffset(section, point, &off));
5840     varr = &vArray[off];
5841     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5842     size += dof;
5843   }
5844   for (p = 0; p < numPoints; ++p) {
5845     const PetscInt     cp = cone[p];
5846     PetscInt           o  = coneO[p];
5847     PetscInt           dof, off, d;
5848     const PetscScalar *varr;
5849 
5850     if ((cp < pStart) || (cp >= pEnd)) continue;
5851     PetscCall(PetscSectionGetDof(section, cp, &dof));
5852     PetscCall(PetscSectionGetOffset(section, cp, &off));
5853     varr = &vArray[off];
5854     if (o >= 0) {
5855       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5856     } else {
5857       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5858     }
5859     size += dof;
5860   }
5861   PetscCall(VecRestoreArrayRead(v, &vArray));
5862   if (!*values) {
5863     if (csize) *csize = size;
5864     *values = array;
5865   } else {
5866     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5867     *csize = size;
5868   }
5869   PetscFunctionReturn(PETSC_SUCCESS);
5870 }
5871 
5872 /* Compress out points not in the section */
5873 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5874 {
5875   const PetscInt np = *numPoints;
5876   PetscInt       pStart, pEnd, p, q;
5877 
5878   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5879   for (p = 0, q = 0; p < np; ++p) {
5880     const PetscInt r = points[p * 2];
5881     if ((r >= pStart) && (r < pEnd)) {
5882       points[q * 2]     = r;
5883       points[q * 2 + 1] = points[p * 2 + 1];
5884       ++q;
5885     }
5886   }
5887   *numPoints = q;
5888   return PETSC_SUCCESS;
5889 }
5890 
5891 /* Compressed closure does not apply closure permutation */
5892 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5893 {
5894   const PetscInt *cla = NULL;
5895   PetscInt        np, *pts = NULL;
5896 
5897   PetscFunctionBeginHot;
5898   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5899   if (!ornt && *clPoints) {
5900     PetscInt dof, off;
5901 
5902     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5903     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5904     PetscCall(ISGetIndices(*clPoints, &cla));
5905     np  = dof / 2;
5906     pts = (PetscInt *)&cla[off];
5907   } else {
5908     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
5909     PetscCall(CompressPoints_Private(section, &np, pts));
5910   }
5911   *numPoints = np;
5912   *points    = pts;
5913   *clp       = cla;
5914   PetscFunctionReturn(PETSC_SUCCESS);
5915 }
5916 
5917 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5918 {
5919   PetscFunctionBeginHot;
5920   if (!*clPoints) {
5921     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5922   } else {
5923     PetscCall(ISRestoreIndices(*clPoints, clp));
5924   }
5925   *numPoints = 0;
5926   *points    = NULL;
5927   *clSec     = NULL;
5928   *clPoints  = NULL;
5929   *clp       = NULL;
5930   PetscFunctionReturn(PETSC_SUCCESS);
5931 }
5932 
5933 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5934 {
5935   PetscInt            offset = 0, p;
5936   const PetscInt    **perms  = NULL;
5937   const PetscScalar **flips  = NULL;
5938 
5939   PetscFunctionBeginHot;
5940   *size = 0;
5941   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5942   for (p = 0; p < numPoints; p++) {
5943     const PetscInt     point = points[2 * p];
5944     const PetscInt    *perm  = perms ? perms[p] : NULL;
5945     const PetscScalar *flip  = flips ? flips[p] : NULL;
5946     PetscInt           dof, off, d;
5947     const PetscScalar *varr;
5948 
5949     PetscCall(PetscSectionGetDof(section, point, &dof));
5950     PetscCall(PetscSectionGetOffset(section, point, &off));
5951     varr = &vArray[off];
5952     if (clperm) {
5953       if (perm) {
5954         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5955       } else {
5956         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5957       }
5958       if (flip) {
5959         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5960       }
5961     } else {
5962       if (perm) {
5963         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5964       } else {
5965         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5966       }
5967       if (flip) {
5968         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5969       }
5970     }
5971     offset += dof;
5972   }
5973   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5974   *size = offset;
5975   PetscFunctionReturn(PETSC_SUCCESS);
5976 }
5977 
5978 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[])
5979 {
5980   PetscInt offset = 0, f;
5981 
5982   PetscFunctionBeginHot;
5983   *size = 0;
5984   for (f = 0; f < numFields; ++f) {
5985     PetscInt            p;
5986     const PetscInt    **perms = NULL;
5987     const PetscScalar **flips = NULL;
5988 
5989     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5990     for (p = 0; p < numPoints; p++) {
5991       const PetscInt     point = points[2 * p];
5992       PetscInt           fdof, foff, b;
5993       const PetscScalar *varr;
5994       const PetscInt    *perm = perms ? perms[p] : NULL;
5995       const PetscScalar *flip = flips ? flips[p] : NULL;
5996 
5997       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5998       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5999       varr = &vArray[foff];
6000       if (clperm) {
6001         if (perm) {
6002           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6003         } else {
6004           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6005         }
6006         if (flip) {
6007           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6008         }
6009       } else {
6010         if (perm) {
6011           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6012         } else {
6013           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6014         }
6015         if (flip) {
6016           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6017         }
6018       }
6019       offset += fdof;
6020     }
6021     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6022   }
6023   *size = offset;
6024   PetscFunctionReturn(PETSC_SUCCESS);
6025 }
6026 
6027 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6028 {
6029   PetscSection    clSection;
6030   IS              clPoints;
6031   PetscInt       *points = NULL;
6032   const PetscInt *clp, *perm = NULL;
6033   PetscInt        depth, numFields, numPoints, asize;
6034 
6035   PetscFunctionBeginHot;
6036   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6037   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6038   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6039   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6040   PetscCall(DMPlexGetDepth(dm, &depth));
6041   PetscCall(PetscSectionGetNumFields(section, &numFields));
6042   if (depth == 1 && numFields < 2) {
6043     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6044     PetscFunctionReturn(PETSC_SUCCESS);
6045   }
6046   /* Get points */
6047   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6048   /* Get sizes */
6049   asize = 0;
6050   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6051     PetscInt dof;
6052     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6053     asize += dof;
6054   }
6055   if (values) {
6056     const PetscScalar *vArray;
6057     PetscInt           size;
6058 
6059     if (*values) {
6060       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);
6061     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6062     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6063     PetscCall(VecGetArrayRead(v, &vArray));
6064     /* Get values */
6065     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6066     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6067     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6068     /* Cleanup array */
6069     PetscCall(VecRestoreArrayRead(v, &vArray));
6070   }
6071   if (csize) *csize = asize;
6072   /* Cleanup points */
6073   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6074   PetscFunctionReturn(PETSC_SUCCESS);
6075 }
6076 
6077 /*@C
6078   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6079 
6080   Not collective
6081 
6082   Input Parameters:
6083 + dm      - The `DM`
6084 . section - The section describing the layout in `v`, or `NULL` to use the default section
6085 . v       - The local vector
6086 - point   - The point in the `DM`
6087 
6088   Input/Output Parameters:
6089 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6090 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6091            if the user provided `NULL`, it is a borrowed array and should not be freed
6092 
6093   Level: intermediate
6094 
6095   Notes:
6096   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6097   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6098   assembly function, and a user may already have allocated storage for this operation.
6099 
6100   A typical use could be
6101 .vb
6102    values = NULL;
6103    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6104    for (cl = 0; cl < clSize; ++cl) {
6105      <Compute on closure>
6106    }
6107    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6108 .ve
6109   or
6110 .vb
6111    PetscMalloc1(clMaxSize, &values);
6112    for (p = pStart; p < pEnd; ++p) {
6113      clSize = clMaxSize;
6114      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6115      for (cl = 0; cl < clSize; ++cl) {
6116        <Compute on closure>
6117      }
6118    }
6119    PetscFree(values);
6120 .ve
6121 
6122   Fortran Notes:
6123   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6124 
6125 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6126 @*/
6127 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6128 {
6129   PetscFunctionBeginHot;
6130   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6131   PetscFunctionReturn(PETSC_SUCCESS);
6132 }
6133 
6134 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6135 {
6136   DMLabel            depthLabel;
6137   PetscSection       clSection;
6138   IS                 clPoints;
6139   PetscScalar       *array;
6140   const PetscScalar *vArray;
6141   PetscInt          *points = NULL;
6142   const PetscInt    *clp, *perm = NULL;
6143   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6144 
6145   PetscFunctionBeginHot;
6146   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6147   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6148   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6149   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6150   PetscCall(DMPlexGetDepth(dm, &mdepth));
6151   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6152   PetscCall(PetscSectionGetNumFields(section, &numFields));
6153   if (mdepth == 1 && numFields < 2) {
6154     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6155     PetscFunctionReturn(PETSC_SUCCESS);
6156   }
6157   /* Get points */
6158   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6159   for (clsize = 0, p = 0; p < Np; p++) {
6160     PetscInt dof;
6161     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6162     clsize += dof;
6163   }
6164   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6165   /* Filter points */
6166   for (p = 0; p < numPoints * 2; p += 2) {
6167     PetscInt dep;
6168 
6169     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6170     if (dep != depth) continue;
6171     points[Np * 2 + 0] = points[p];
6172     points[Np * 2 + 1] = points[p + 1];
6173     ++Np;
6174   }
6175   /* Get array */
6176   if (!values || !*values) {
6177     PetscInt asize = 0, dof;
6178 
6179     for (p = 0; p < Np * 2; p += 2) {
6180       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6181       asize += dof;
6182     }
6183     if (!values) {
6184       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6185       if (csize) *csize = asize;
6186       PetscFunctionReturn(PETSC_SUCCESS);
6187     }
6188     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6189   } else {
6190     array = *values;
6191   }
6192   PetscCall(VecGetArrayRead(v, &vArray));
6193   /* Get values */
6194   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6195   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6196   /* Cleanup points */
6197   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6198   /* Cleanup array */
6199   PetscCall(VecRestoreArrayRead(v, &vArray));
6200   if (!*values) {
6201     if (csize) *csize = size;
6202     *values = array;
6203   } else {
6204     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6205     *csize = size;
6206   }
6207   PetscFunctionReturn(PETSC_SUCCESS);
6208 }
6209 
6210 /*@C
6211   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6212 
6213   Not collective
6214 
6215   Input Parameters:
6216 + dm      - The `DM`
6217 . section - The section describing the layout in `v`, or `NULL` to use the default section
6218 . v       - The local vector
6219 . point   - The point in the `DM`
6220 . csize   - The number of values in the closure, or `NULL`
6221 - values  - The array of values, which is a borrowed array and should not be freed
6222 
6223   Level: intermediate
6224 
6225   Note:
6226   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6227 
6228   Fortran Notes:
6229   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6230 
6231 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6232 @*/
6233 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6234 {
6235   PetscInt size = 0;
6236 
6237   PetscFunctionBegin;
6238   /* Should work without recalculating size */
6239   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6240   *values = NULL;
6241   PetscFunctionReturn(PETSC_SUCCESS);
6242 }
6243 
6244 static inline void add(PetscScalar *x, PetscScalar y)
6245 {
6246   *x += y;
6247 }
6248 static inline void insert(PetscScalar *x, PetscScalar y)
6249 {
6250   *x = y;
6251 }
6252 
6253 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[])
6254 {
6255   PetscInt        cdof;  /* The number of constraints on this point */
6256   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6257   PetscScalar    *a;
6258   PetscInt        off, cind = 0, k;
6259 
6260   PetscFunctionBegin;
6261   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6262   PetscCall(PetscSectionGetOffset(section, point, &off));
6263   a = &array[off];
6264   if (!cdof || setBC) {
6265     if (clperm) {
6266       if (perm) {
6267         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6268       } else {
6269         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6270       }
6271     } else {
6272       if (perm) {
6273         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6274       } else {
6275         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6276       }
6277     }
6278   } else {
6279     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6280     if (clperm) {
6281       if (perm) {
6282         for (k = 0; k < dof; ++k) {
6283           if ((cind < cdof) && (k == cdofs[cind])) {
6284             ++cind;
6285             continue;
6286           }
6287           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6288         }
6289       } else {
6290         for (k = 0; k < dof; ++k) {
6291           if ((cind < cdof) && (k == cdofs[cind])) {
6292             ++cind;
6293             continue;
6294           }
6295           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6296         }
6297       }
6298     } else {
6299       if (perm) {
6300         for (k = 0; k < dof; ++k) {
6301           if ((cind < cdof) && (k == cdofs[cind])) {
6302             ++cind;
6303             continue;
6304           }
6305           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6306         }
6307       } else {
6308         for (k = 0; k < dof; ++k) {
6309           if ((cind < cdof) && (k == cdofs[cind])) {
6310             ++cind;
6311             continue;
6312           }
6313           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6314         }
6315       }
6316     }
6317   }
6318   PetscFunctionReturn(PETSC_SUCCESS);
6319 }
6320 
6321 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[])
6322 {
6323   PetscInt        cdof;  /* The number of constraints on this point */
6324   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6325   PetscScalar    *a;
6326   PetscInt        off, cind = 0, k;
6327 
6328   PetscFunctionBegin;
6329   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6330   PetscCall(PetscSectionGetOffset(section, point, &off));
6331   a = &array[off];
6332   if (cdof) {
6333     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6334     if (clperm) {
6335       if (perm) {
6336         for (k = 0; k < dof; ++k) {
6337           if ((cind < cdof) && (k == cdofs[cind])) {
6338             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6339             cind++;
6340           }
6341         }
6342       } else {
6343         for (k = 0; k < dof; ++k) {
6344           if ((cind < cdof) && (k == cdofs[cind])) {
6345             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6346             cind++;
6347           }
6348         }
6349       }
6350     } else {
6351       if (perm) {
6352         for (k = 0; k < dof; ++k) {
6353           if ((cind < cdof) && (k == cdofs[cind])) {
6354             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6355             cind++;
6356           }
6357         }
6358       } else {
6359         for (k = 0; k < dof; ++k) {
6360           if ((cind < cdof) && (k == cdofs[cind])) {
6361             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6362             cind++;
6363           }
6364         }
6365       }
6366     }
6367   }
6368   PetscFunctionReturn(PETSC_SUCCESS);
6369 }
6370 
6371 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[])
6372 {
6373   PetscScalar    *a;
6374   PetscInt        fdof, foff, fcdof, foffset = *offset;
6375   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6376   PetscInt        cind = 0, b;
6377 
6378   PetscFunctionBegin;
6379   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6380   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6381   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6382   a = &array[foff];
6383   if (!fcdof || setBC) {
6384     if (clperm) {
6385       if (perm) {
6386         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6387       } else {
6388         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6389       }
6390     } else {
6391       if (perm) {
6392         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6393       } else {
6394         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6395       }
6396     }
6397   } else {
6398     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6399     if (clperm) {
6400       if (perm) {
6401         for (b = 0; b < fdof; b++) {
6402           if ((cind < fcdof) && (b == fcdofs[cind])) {
6403             ++cind;
6404             continue;
6405           }
6406           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6407         }
6408       } else {
6409         for (b = 0; b < fdof; b++) {
6410           if ((cind < fcdof) && (b == fcdofs[cind])) {
6411             ++cind;
6412             continue;
6413           }
6414           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6415         }
6416       }
6417     } else {
6418       if (perm) {
6419         for (b = 0; b < fdof; b++) {
6420           if ((cind < fcdof) && (b == fcdofs[cind])) {
6421             ++cind;
6422             continue;
6423           }
6424           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6425         }
6426       } else {
6427         for (b = 0; b < fdof; b++) {
6428           if ((cind < fcdof) && (b == fcdofs[cind])) {
6429             ++cind;
6430             continue;
6431           }
6432           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6433         }
6434       }
6435     }
6436   }
6437   *offset += fdof;
6438   PetscFunctionReturn(PETSC_SUCCESS);
6439 }
6440 
6441 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[])
6442 {
6443   PetscScalar    *a;
6444   PetscInt        fdof, foff, fcdof, foffset = *offset;
6445   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6446   PetscInt        Nc, cind = 0, ncind = 0, b;
6447   PetscBool       ncSet, fcSet;
6448 
6449   PetscFunctionBegin;
6450   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6451   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6452   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6453   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6454   a = &array[foff];
6455   if (fcdof) {
6456     /* We just override fcdof and fcdofs with Ncc and comps */
6457     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6458     if (clperm) {
6459       if (perm) {
6460         if (comps) {
6461           for (b = 0; b < fdof; b++) {
6462             ncSet = fcSet = PETSC_FALSE;
6463             if (b % Nc == comps[ncind]) {
6464               ncind = (ncind + 1) % Ncc;
6465               ncSet = PETSC_TRUE;
6466             }
6467             if ((cind < fcdof) && (b == fcdofs[cind])) {
6468               ++cind;
6469               fcSet = PETSC_TRUE;
6470             }
6471             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6472           }
6473         } else {
6474           for (b = 0; b < fdof; b++) {
6475             if ((cind < fcdof) && (b == fcdofs[cind])) {
6476               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6477               ++cind;
6478             }
6479           }
6480         }
6481       } else {
6482         if (comps) {
6483           for (b = 0; b < fdof; b++) {
6484             ncSet = fcSet = PETSC_FALSE;
6485             if (b % Nc == comps[ncind]) {
6486               ncind = (ncind + 1) % Ncc;
6487               ncSet = PETSC_TRUE;
6488             }
6489             if ((cind < fcdof) && (b == fcdofs[cind])) {
6490               ++cind;
6491               fcSet = PETSC_TRUE;
6492             }
6493             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6494           }
6495         } else {
6496           for (b = 0; b < fdof; b++) {
6497             if ((cind < fcdof) && (b == fcdofs[cind])) {
6498               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6499               ++cind;
6500             }
6501           }
6502         }
6503       }
6504     } else {
6505       if (perm) {
6506         if (comps) {
6507           for (b = 0; b < fdof; b++) {
6508             ncSet = fcSet = PETSC_FALSE;
6509             if (b % Nc == comps[ncind]) {
6510               ncind = (ncind + 1) % Ncc;
6511               ncSet = PETSC_TRUE;
6512             }
6513             if ((cind < fcdof) && (b == fcdofs[cind])) {
6514               ++cind;
6515               fcSet = PETSC_TRUE;
6516             }
6517             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6518           }
6519         } else {
6520           for (b = 0; b < fdof; b++) {
6521             if ((cind < fcdof) && (b == fcdofs[cind])) {
6522               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6523               ++cind;
6524             }
6525           }
6526         }
6527       } else {
6528         if (comps) {
6529           for (b = 0; b < fdof; b++) {
6530             ncSet = fcSet = PETSC_FALSE;
6531             if (b % Nc == comps[ncind]) {
6532               ncind = (ncind + 1) % Ncc;
6533               ncSet = PETSC_TRUE;
6534             }
6535             if ((cind < fcdof) && (b == fcdofs[cind])) {
6536               ++cind;
6537               fcSet = PETSC_TRUE;
6538             }
6539             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6540           }
6541         } else {
6542           for (b = 0; b < fdof; b++) {
6543             if ((cind < fcdof) && (b == fcdofs[cind])) {
6544               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6545               ++cind;
6546             }
6547           }
6548         }
6549       }
6550     }
6551   }
6552   *offset += fdof;
6553   PetscFunctionReturn(PETSC_SUCCESS);
6554 }
6555 
6556 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6557 {
6558   PetscScalar    *array;
6559   const PetscInt *cone, *coneO;
6560   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6561 
6562   PetscFunctionBeginHot;
6563   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6564   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6565   PetscCall(DMPlexGetCone(dm, point, &cone));
6566   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6567   PetscCall(VecGetArray(v, &array));
6568   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6569     const PetscInt cp = !p ? point : cone[p - 1];
6570     const PetscInt o  = !p ? 0 : coneO[p - 1];
6571 
6572     if ((cp < pStart) || (cp >= pEnd)) {
6573       dof = 0;
6574       continue;
6575     }
6576     PetscCall(PetscSectionGetDof(section, cp, &dof));
6577     /* ADD_VALUES */
6578     {
6579       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6580       PetscScalar    *a;
6581       PetscInt        cdof, coff, cind = 0, k;
6582 
6583       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6584       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6585       a = &array[coff];
6586       if (!cdof) {
6587         if (o >= 0) {
6588           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6589         } else {
6590           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6591         }
6592       } else {
6593         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6594         if (o >= 0) {
6595           for (k = 0; k < dof; ++k) {
6596             if ((cind < cdof) && (k == cdofs[cind])) {
6597               ++cind;
6598               continue;
6599             }
6600             a[k] += values[off + k];
6601           }
6602         } else {
6603           for (k = 0; k < dof; ++k) {
6604             if ((cind < cdof) && (k == cdofs[cind])) {
6605               ++cind;
6606               continue;
6607             }
6608             a[k] += values[off + dof - k - 1];
6609           }
6610         }
6611       }
6612     }
6613   }
6614   PetscCall(VecRestoreArray(v, &array));
6615   PetscFunctionReturn(PETSC_SUCCESS);
6616 }
6617 
6618 /*@C
6619   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6620 
6621   Not collective
6622 
6623   Input Parameters:
6624 + dm      - The `DM`
6625 . section - The section describing the layout in `v`, or `NULL` to use the default section
6626 . v       - The local vector
6627 . point   - The point in the `DM`
6628 . values  - The array of values
6629 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6630          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6631 
6632   Level: intermediate
6633 
6634 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6635 @*/
6636 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6637 {
6638   PetscSection    clSection;
6639   IS              clPoints;
6640   PetscScalar    *array;
6641   PetscInt       *points = NULL;
6642   const PetscInt *clp, *clperm = NULL;
6643   PetscInt        depth, numFields, numPoints, p, clsize;
6644 
6645   PetscFunctionBeginHot;
6646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6647   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6648   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6649   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6650   PetscCall(DMPlexGetDepth(dm, &depth));
6651   PetscCall(PetscSectionGetNumFields(section, &numFields));
6652   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6653     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6654     PetscFunctionReturn(PETSC_SUCCESS);
6655   }
6656   /* Get points */
6657   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6658   for (clsize = 0, p = 0; p < numPoints; p++) {
6659     PetscInt dof;
6660     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6661     clsize += dof;
6662   }
6663   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6664   /* Get array */
6665   PetscCall(VecGetArray(v, &array));
6666   /* Get values */
6667   if (numFields > 0) {
6668     PetscInt offset = 0, f;
6669     for (f = 0; f < numFields; ++f) {
6670       const PetscInt    **perms = NULL;
6671       const PetscScalar **flips = NULL;
6672 
6673       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6674       switch (mode) {
6675       case INSERT_VALUES:
6676         for (p = 0; p < numPoints; p++) {
6677           const PetscInt     point = points[2 * p];
6678           const PetscInt    *perm  = perms ? perms[p] : NULL;
6679           const PetscScalar *flip  = flips ? flips[p] : NULL;
6680           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6681         }
6682         break;
6683       case INSERT_ALL_VALUES:
6684         for (p = 0; p < numPoints; p++) {
6685           const PetscInt     point = points[2 * p];
6686           const PetscInt    *perm  = perms ? perms[p] : NULL;
6687           const PetscScalar *flip  = flips ? flips[p] : NULL;
6688           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6689         }
6690         break;
6691       case INSERT_BC_VALUES:
6692         for (p = 0; p < numPoints; p++) {
6693           const PetscInt     point = points[2 * p];
6694           const PetscInt    *perm  = perms ? perms[p] : NULL;
6695           const PetscScalar *flip  = flips ? flips[p] : NULL;
6696           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6697         }
6698         break;
6699       case ADD_VALUES:
6700         for (p = 0; p < numPoints; p++) {
6701           const PetscInt     point = points[2 * p];
6702           const PetscInt    *perm  = perms ? perms[p] : NULL;
6703           const PetscScalar *flip  = flips ? flips[p] : NULL;
6704           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6705         }
6706         break;
6707       case ADD_ALL_VALUES:
6708         for (p = 0; p < numPoints; p++) {
6709           const PetscInt     point = points[2 * p];
6710           const PetscInt    *perm  = perms ? perms[p] : NULL;
6711           const PetscScalar *flip  = flips ? flips[p] : NULL;
6712           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6713         }
6714         break;
6715       case ADD_BC_VALUES:
6716         for (p = 0; p < numPoints; p++) {
6717           const PetscInt     point = points[2 * p];
6718           const PetscInt    *perm  = perms ? perms[p] : NULL;
6719           const PetscScalar *flip  = flips ? flips[p] : NULL;
6720           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6721         }
6722         break;
6723       default:
6724         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6725       }
6726       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6727     }
6728   } else {
6729     PetscInt            dof, off;
6730     const PetscInt    **perms = NULL;
6731     const PetscScalar **flips = NULL;
6732 
6733     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6734     switch (mode) {
6735     case INSERT_VALUES:
6736       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6737         const PetscInt     point = points[2 * p];
6738         const PetscInt    *perm  = perms ? perms[p] : NULL;
6739         const PetscScalar *flip  = flips ? flips[p] : NULL;
6740         PetscCall(PetscSectionGetDof(section, point, &dof));
6741         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6742       }
6743       break;
6744     case INSERT_ALL_VALUES:
6745       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6746         const PetscInt     point = points[2 * p];
6747         const PetscInt    *perm  = perms ? perms[p] : NULL;
6748         const PetscScalar *flip  = flips ? flips[p] : NULL;
6749         PetscCall(PetscSectionGetDof(section, point, &dof));
6750         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6751       }
6752       break;
6753     case INSERT_BC_VALUES:
6754       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6755         const PetscInt     point = points[2 * p];
6756         const PetscInt    *perm  = perms ? perms[p] : NULL;
6757         const PetscScalar *flip  = flips ? flips[p] : NULL;
6758         PetscCall(PetscSectionGetDof(section, point, &dof));
6759         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6760       }
6761       break;
6762     case ADD_VALUES:
6763       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6764         const PetscInt     point = points[2 * p];
6765         const PetscInt    *perm  = perms ? perms[p] : NULL;
6766         const PetscScalar *flip  = flips ? flips[p] : NULL;
6767         PetscCall(PetscSectionGetDof(section, point, &dof));
6768         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6769       }
6770       break;
6771     case ADD_ALL_VALUES:
6772       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6773         const PetscInt     point = points[2 * p];
6774         const PetscInt    *perm  = perms ? perms[p] : NULL;
6775         const PetscScalar *flip  = flips ? flips[p] : NULL;
6776         PetscCall(PetscSectionGetDof(section, point, &dof));
6777         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6778       }
6779       break;
6780     case ADD_BC_VALUES:
6781       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6782         const PetscInt     point = points[2 * p];
6783         const PetscInt    *perm  = perms ? perms[p] : NULL;
6784         const PetscScalar *flip  = flips ? flips[p] : NULL;
6785         PetscCall(PetscSectionGetDof(section, point, &dof));
6786         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6787       }
6788       break;
6789     default:
6790       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6791     }
6792     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6793   }
6794   /* Cleanup points */
6795   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6796   /* Cleanup array */
6797   PetscCall(VecRestoreArray(v, &array));
6798   PetscFunctionReturn(PETSC_SUCCESS);
6799 }
6800 
6801 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6802 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6803 {
6804   PetscFunctionBegin;
6805   *contains = PETSC_TRUE;
6806   if (label) {
6807     PetscInt fdof;
6808 
6809     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6810     if (!*contains) {
6811       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6812       *offset += fdof;
6813       PetscFunctionReturn(PETSC_SUCCESS);
6814     }
6815   }
6816   PetscFunctionReturn(PETSC_SUCCESS);
6817 }
6818 
6819 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6820 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)
6821 {
6822   PetscSection    clSection;
6823   IS              clPoints;
6824   PetscScalar    *array;
6825   PetscInt       *points = NULL;
6826   const PetscInt *clp;
6827   PetscInt        numFields, numPoints, p;
6828   PetscInt        offset = 0, f;
6829 
6830   PetscFunctionBeginHot;
6831   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6832   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6833   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6834   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6835   PetscCall(PetscSectionGetNumFields(section, &numFields));
6836   /* Get points */
6837   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6838   /* Get array */
6839   PetscCall(VecGetArray(v, &array));
6840   /* Get values */
6841   for (f = 0; f < numFields; ++f) {
6842     const PetscInt    **perms = NULL;
6843     const PetscScalar **flips = NULL;
6844     PetscBool           contains;
6845 
6846     if (!fieldActive[f]) {
6847       for (p = 0; p < numPoints * 2; p += 2) {
6848         PetscInt fdof;
6849         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6850         offset += fdof;
6851       }
6852       continue;
6853     }
6854     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6855     switch (mode) {
6856     case INSERT_VALUES:
6857       for (p = 0; p < numPoints; p++) {
6858         const PetscInt     point = points[2 * p];
6859         const PetscInt    *perm  = perms ? perms[p] : NULL;
6860         const PetscScalar *flip  = flips ? flips[p] : NULL;
6861         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6862         if (!contains) continue;
6863         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6864       }
6865       break;
6866     case INSERT_ALL_VALUES:
6867       for (p = 0; p < numPoints; p++) {
6868         const PetscInt     point = points[2 * p];
6869         const PetscInt    *perm  = perms ? perms[p] : NULL;
6870         const PetscScalar *flip  = flips ? flips[p] : NULL;
6871         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6872         if (!contains) continue;
6873         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6874       }
6875       break;
6876     case INSERT_BC_VALUES:
6877       for (p = 0; p < numPoints; p++) {
6878         const PetscInt     point = points[2 * p];
6879         const PetscInt    *perm  = perms ? perms[p] : NULL;
6880         const PetscScalar *flip  = flips ? flips[p] : NULL;
6881         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6882         if (!contains) continue;
6883         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6884       }
6885       break;
6886     case ADD_VALUES:
6887       for (p = 0; p < numPoints; p++) {
6888         const PetscInt     point = points[2 * p];
6889         const PetscInt    *perm  = perms ? perms[p] : NULL;
6890         const PetscScalar *flip  = flips ? flips[p] : NULL;
6891         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6892         if (!contains) continue;
6893         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6894       }
6895       break;
6896     case ADD_ALL_VALUES:
6897       for (p = 0; p < numPoints; p++) {
6898         const PetscInt     point = points[2 * p];
6899         const PetscInt    *perm  = perms ? perms[p] : NULL;
6900         const PetscScalar *flip  = flips ? flips[p] : NULL;
6901         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6902         if (!contains) continue;
6903         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6904       }
6905       break;
6906     default:
6907       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6908     }
6909     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6910   }
6911   /* Cleanup points */
6912   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6913   /* Cleanup array */
6914   PetscCall(VecRestoreArray(v, &array));
6915   PetscFunctionReturn(PETSC_SUCCESS);
6916 }
6917 
6918 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6919 {
6920   PetscMPIInt rank;
6921   PetscInt    i, j;
6922 
6923   PetscFunctionBegin;
6924   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6925   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6926   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6927   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6928   numCIndices = numCIndices ? numCIndices : numRIndices;
6929   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6930   for (i = 0; i < numRIndices; i++) {
6931     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6932     for (j = 0; j < numCIndices; j++) {
6933 #if defined(PETSC_USE_COMPLEX)
6934       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6935 #else
6936       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6937 #endif
6938     }
6939     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6940   }
6941   PetscFunctionReturn(PETSC_SUCCESS);
6942 }
6943 
6944 /*
6945   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6946 
6947   Input Parameters:
6948 + section - The section for this data layout
6949 . islocal - Is the section (and thus indices being requested) local or global?
6950 . point   - The point contributing dofs with these indices
6951 . off     - The global offset of this point
6952 . loff    - The local offset of each field
6953 . setBC   - The flag determining whether to include indices of boundary values
6954 . perm    - A permutation of the dofs on this point, or NULL
6955 - indperm - A permutation of the entire indices array, or NULL
6956 
6957   Output Parameter:
6958 . indices - Indices for dofs on this point
6959 
6960   Level: developer
6961 
6962   Note: The indices could be local or global, depending on the value of 'off'.
6963 */
6964 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6965 {
6966   PetscInt        dof;   /* The number of unknowns on this point */
6967   PetscInt        cdof;  /* The number of constraints on this point */
6968   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6969   PetscInt        cind = 0, k;
6970 
6971   PetscFunctionBegin;
6972   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6973   PetscCall(PetscSectionGetDof(section, point, &dof));
6974   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6975   if (!cdof || setBC) {
6976     for (k = 0; k < dof; ++k) {
6977       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6978       const PetscInt ind    = indperm ? indperm[preind] : preind;
6979 
6980       indices[ind] = off + k;
6981     }
6982   } else {
6983     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6984     for (k = 0; k < dof; ++k) {
6985       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6986       const PetscInt ind    = indperm ? indperm[preind] : preind;
6987 
6988       if ((cind < cdof) && (k == cdofs[cind])) {
6989         /* Insert check for returning constrained indices */
6990         indices[ind] = -(off + k + 1);
6991         ++cind;
6992       } else {
6993         indices[ind] = off + k - (islocal ? 0 : cind);
6994       }
6995     }
6996   }
6997   *loff += dof;
6998   PetscFunctionReturn(PETSC_SUCCESS);
6999 }
7000 
7001 /*
7002  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7003 
7004  Input Parameters:
7005 + section - a section (global or local)
7006 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7007 . point - point within section
7008 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7009 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7010 . setBC - identify constrained (boundary condition) points via involution.
7011 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7012 . permsoff - offset
7013 - indperm - index permutation
7014 
7015  Output Parameter:
7016 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7017 . indices - array to hold indices (as defined by section) of each dof associated with point
7018 
7019  Notes:
7020  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7021  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7022  in the local vector.
7023 
7024  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7025  significant).  It is invalid to call with a global section and setBC=true.
7026 
7027  Developer Note:
7028  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7029  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7030  offset could be obtained from the section instead of passing it explicitly as we do now.
7031 
7032  Example:
7033  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7034  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7035  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7036  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.
7037 
7038  Level: developer
7039 */
7040 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[])
7041 {
7042   PetscInt numFields, foff, f;
7043 
7044   PetscFunctionBegin;
7045   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7046   PetscCall(PetscSectionGetNumFields(section, &numFields));
7047   for (f = 0, foff = 0; f < numFields; ++f) {
7048     PetscInt        fdof, cfdof;
7049     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7050     PetscInt        cind = 0, b;
7051     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7052 
7053     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7054     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7055     if (!cfdof || setBC) {
7056       for (b = 0; b < fdof; ++b) {
7057         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7058         const PetscInt ind    = indperm ? indperm[preind] : preind;
7059 
7060         indices[ind] = off + foff + b;
7061       }
7062     } else {
7063       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7064       for (b = 0; b < fdof; ++b) {
7065         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7066         const PetscInt ind    = indperm ? indperm[preind] : preind;
7067 
7068         if ((cind < cfdof) && (b == fcdofs[cind])) {
7069           indices[ind] = -(off + foff + b + 1);
7070           ++cind;
7071         } else {
7072           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7073         }
7074       }
7075     }
7076     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7077     foffs[f] += fdof;
7078   }
7079   PetscFunctionReturn(PETSC_SUCCESS);
7080 }
7081 
7082 /*
7083   This version believes the globalSection offsets for each field, rather than just the point offset
7084 
7085  . foffs - The offset into 'indices' for each field, since it is segregated by field
7086 
7087  Notes:
7088  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7089  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7090 */
7091 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7092 {
7093   PetscInt numFields, foff, f;
7094 
7095   PetscFunctionBegin;
7096   PetscCall(PetscSectionGetNumFields(section, &numFields));
7097   for (f = 0; f < numFields; ++f) {
7098     PetscInt        fdof, cfdof;
7099     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7100     PetscInt        cind = 0, b;
7101     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7102 
7103     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7104     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7105     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7106     if (!cfdof) {
7107       for (b = 0; b < fdof; ++b) {
7108         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7109         const PetscInt ind    = indperm ? indperm[preind] : preind;
7110 
7111         indices[ind] = foff + b;
7112       }
7113     } else {
7114       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7115       for (b = 0; b < fdof; ++b) {
7116         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7117         const PetscInt ind    = indperm ? indperm[preind] : preind;
7118 
7119         if ((cind < cfdof) && (b == fcdofs[cind])) {
7120           indices[ind] = -(foff + b + 1);
7121           ++cind;
7122         } else {
7123           indices[ind] = foff + b - cind;
7124         }
7125       }
7126     }
7127     foffs[f] += fdof;
7128   }
7129   PetscFunctionReturn(PETSC_SUCCESS);
7130 }
7131 
7132 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)
7133 {
7134   Mat             cMat;
7135   PetscSection    aSec, cSec;
7136   IS              aIS;
7137   PetscInt        aStart = -1, aEnd = -1;
7138   const PetscInt *anchors;
7139   PetscInt        numFields, f, p, q, newP = 0;
7140   PetscInt        newNumPoints = 0, newNumIndices = 0;
7141   PetscInt       *newPoints, *indices, *newIndices;
7142   PetscInt        maxAnchor, maxDof;
7143   PetscInt        newOffsets[32];
7144   PetscInt       *pointMatOffsets[32];
7145   PetscInt       *newPointOffsets[32];
7146   PetscScalar    *pointMat[32];
7147   PetscScalar    *newValues      = NULL, *tmpValues;
7148   PetscBool       anyConstrained = PETSC_FALSE;
7149 
7150   PetscFunctionBegin;
7151   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7152   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7153   PetscCall(PetscSectionGetNumFields(section, &numFields));
7154 
7155   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7156   /* if there are point-to-point constraints */
7157   if (aSec) {
7158     PetscCall(PetscArrayzero(newOffsets, 32));
7159     PetscCall(ISGetIndices(aIS, &anchors));
7160     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7161     /* figure out how many points are going to be in the new element matrix
7162      * (we allow double counting, because it's all just going to be summed
7163      * into the global matrix anyway) */
7164     for (p = 0; p < 2 * numPoints; p += 2) {
7165       PetscInt b    = points[p];
7166       PetscInt bDof = 0, bSecDof;
7167 
7168       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7169       if (!bSecDof) continue;
7170       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7171       if (bDof) {
7172         /* this point is constrained */
7173         /* it is going to be replaced by its anchors */
7174         PetscInt bOff, q;
7175 
7176         anyConstrained = PETSC_TRUE;
7177         newNumPoints += bDof;
7178         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7179         for (q = 0; q < bDof; q++) {
7180           PetscInt a = anchors[bOff + q];
7181           PetscInt aDof;
7182 
7183           PetscCall(PetscSectionGetDof(section, a, &aDof));
7184           newNumIndices += aDof;
7185           for (f = 0; f < numFields; ++f) {
7186             PetscInt fDof;
7187 
7188             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7189             newOffsets[f + 1] += fDof;
7190           }
7191         }
7192       } else {
7193         /* this point is not constrained */
7194         newNumPoints++;
7195         newNumIndices += bSecDof;
7196         for (f = 0; f < numFields; ++f) {
7197           PetscInt fDof;
7198 
7199           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7200           newOffsets[f + 1] += fDof;
7201         }
7202       }
7203     }
7204   }
7205   if (!anyConstrained) {
7206     if (outNumPoints) *outNumPoints = 0;
7207     if (outNumIndices) *outNumIndices = 0;
7208     if (outPoints) *outPoints = NULL;
7209     if (outValues) *outValues = NULL;
7210     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7211     PetscFunctionReturn(PETSC_SUCCESS);
7212   }
7213 
7214   if (outNumPoints) *outNumPoints = newNumPoints;
7215   if (outNumIndices) *outNumIndices = newNumIndices;
7216 
7217   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7218 
7219   if (!outPoints && !outValues) {
7220     if (offsets) {
7221       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7222     }
7223     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7224     PetscFunctionReturn(PETSC_SUCCESS);
7225   }
7226 
7227   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7228 
7229   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7230 
7231   /* workspaces */
7232   if (numFields) {
7233     for (f = 0; f < numFields; f++) {
7234       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7235       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7236     }
7237   } else {
7238     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7239     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7240   }
7241 
7242   /* get workspaces for the point-to-point matrices */
7243   if (numFields) {
7244     PetscInt totalOffset, totalMatOffset;
7245 
7246     for (p = 0; p < numPoints; p++) {
7247       PetscInt b    = points[2 * p];
7248       PetscInt bDof = 0, bSecDof;
7249 
7250       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7251       if (!bSecDof) {
7252         for (f = 0; f < numFields; f++) {
7253           newPointOffsets[f][p + 1] = 0;
7254           pointMatOffsets[f][p + 1] = 0;
7255         }
7256         continue;
7257       }
7258       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7259       if (bDof) {
7260         for (f = 0; f < numFields; f++) {
7261           PetscInt fDof, q, bOff, allFDof = 0;
7262 
7263           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7264           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7265           for (q = 0; q < bDof; q++) {
7266             PetscInt a = anchors[bOff + q];
7267             PetscInt aFDof;
7268 
7269             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7270             allFDof += aFDof;
7271           }
7272           newPointOffsets[f][p + 1] = allFDof;
7273           pointMatOffsets[f][p + 1] = fDof * allFDof;
7274         }
7275       } else {
7276         for (f = 0; f < numFields; f++) {
7277           PetscInt fDof;
7278 
7279           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7280           newPointOffsets[f][p + 1] = fDof;
7281           pointMatOffsets[f][p + 1] = 0;
7282         }
7283       }
7284     }
7285     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7286       newPointOffsets[f][0] = totalOffset;
7287       pointMatOffsets[f][0] = totalMatOffset;
7288       for (p = 0; p < numPoints; p++) {
7289         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7290         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7291       }
7292       totalOffset    = newPointOffsets[f][numPoints];
7293       totalMatOffset = pointMatOffsets[f][numPoints];
7294       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7295     }
7296   } else {
7297     for (p = 0; p < numPoints; p++) {
7298       PetscInt b    = points[2 * p];
7299       PetscInt bDof = 0, bSecDof;
7300 
7301       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7302       if (!bSecDof) {
7303         newPointOffsets[0][p + 1] = 0;
7304         pointMatOffsets[0][p + 1] = 0;
7305         continue;
7306       }
7307       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7308       if (bDof) {
7309         PetscInt bOff, q, allDof = 0;
7310 
7311         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7312         for (q = 0; q < bDof; q++) {
7313           PetscInt a = anchors[bOff + q], aDof;
7314 
7315           PetscCall(PetscSectionGetDof(section, a, &aDof));
7316           allDof += aDof;
7317         }
7318         newPointOffsets[0][p + 1] = allDof;
7319         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7320       } else {
7321         newPointOffsets[0][p + 1] = bSecDof;
7322         pointMatOffsets[0][p + 1] = 0;
7323       }
7324     }
7325     newPointOffsets[0][0] = 0;
7326     pointMatOffsets[0][0] = 0;
7327     for (p = 0; p < numPoints; p++) {
7328       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7329       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7330     }
7331     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7332   }
7333 
7334   /* output arrays */
7335   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7336 
7337   /* get the point-to-point matrices; construct newPoints */
7338   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7339   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7340   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7341   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7342   if (numFields) {
7343     for (p = 0, newP = 0; p < numPoints; p++) {
7344       PetscInt b    = points[2 * p];
7345       PetscInt o    = points[2 * p + 1];
7346       PetscInt bDof = 0, bSecDof;
7347 
7348       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7349       if (!bSecDof) continue;
7350       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7351       if (bDof) {
7352         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7353 
7354         fStart[0] = 0;
7355         fEnd[0]   = 0;
7356         for (f = 0; f < numFields; f++) {
7357           PetscInt fDof;
7358 
7359           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7360           fStart[f + 1] = fStart[f] + fDof;
7361           fEnd[f + 1]   = fStart[f + 1];
7362         }
7363         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7364         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7365 
7366         fAnchorStart[0] = 0;
7367         fAnchorEnd[0]   = 0;
7368         for (f = 0; f < numFields; f++) {
7369           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7370 
7371           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7372           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7373         }
7374         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7375         for (q = 0; q < bDof; q++) {
7376           PetscInt a = anchors[bOff + q], aOff;
7377 
7378           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7379           newPoints[2 * (newP + q)]     = a;
7380           newPoints[2 * (newP + q) + 1] = 0;
7381           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7382           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7383         }
7384         newP += bDof;
7385 
7386         if (outValues) {
7387           /* get the point-to-point submatrix */
7388           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]));
7389         }
7390       } else {
7391         newPoints[2 * newP]     = b;
7392         newPoints[2 * newP + 1] = o;
7393         newP++;
7394       }
7395     }
7396   } else {
7397     for (p = 0; p < numPoints; p++) {
7398       PetscInt b    = points[2 * p];
7399       PetscInt o    = points[2 * p + 1];
7400       PetscInt bDof = 0, bSecDof;
7401 
7402       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7403       if (!bSecDof) continue;
7404       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7405       if (bDof) {
7406         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7407 
7408         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7409         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7410 
7411         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7412         for (q = 0; q < bDof; q++) {
7413           PetscInt a = anchors[bOff + q], aOff;
7414 
7415           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7416 
7417           newPoints[2 * (newP + q)]     = a;
7418           newPoints[2 * (newP + q) + 1] = 0;
7419           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7420           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7421         }
7422         newP += bDof;
7423 
7424         /* get the point-to-point submatrix */
7425         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7426       } else {
7427         newPoints[2 * newP]     = b;
7428         newPoints[2 * newP + 1] = o;
7429         newP++;
7430       }
7431     }
7432   }
7433 
7434   if (outValues) {
7435     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7436     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7437     /* multiply constraints on the right */
7438     if (numFields) {
7439       for (f = 0; f < numFields; f++) {
7440         PetscInt oldOff = offsets[f];
7441 
7442         for (p = 0; p < numPoints; p++) {
7443           PetscInt cStart = newPointOffsets[f][p];
7444           PetscInt b      = points[2 * p];
7445           PetscInt c, r, k;
7446           PetscInt dof;
7447 
7448           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7449           if (!dof) continue;
7450           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7451             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7452             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7453 
7454             for (r = 0; r < numIndices; r++) {
7455               for (c = 0; c < nCols; c++) {
7456                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7457               }
7458             }
7459           } else {
7460             /* copy this column as is */
7461             for (r = 0; r < numIndices; r++) {
7462               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7463             }
7464           }
7465           oldOff += dof;
7466         }
7467       }
7468     } else {
7469       PetscInt oldOff = 0;
7470       for (p = 0; p < numPoints; p++) {
7471         PetscInt cStart = newPointOffsets[0][p];
7472         PetscInt b      = points[2 * p];
7473         PetscInt c, r, k;
7474         PetscInt dof;
7475 
7476         PetscCall(PetscSectionGetDof(section, b, &dof));
7477         if (!dof) continue;
7478         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7479           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7480           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7481 
7482           for (r = 0; r < numIndices; r++) {
7483             for (c = 0; c < nCols; c++) {
7484               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7485             }
7486           }
7487         } else {
7488           /* copy this column as is */
7489           for (r = 0; r < numIndices; r++) {
7490             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7491           }
7492         }
7493         oldOff += dof;
7494       }
7495     }
7496 
7497     if (multiplyLeft) {
7498       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7499       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7500       /* multiply constraints transpose on the left */
7501       if (numFields) {
7502         for (f = 0; f < numFields; f++) {
7503           PetscInt oldOff = offsets[f];
7504 
7505           for (p = 0; p < numPoints; p++) {
7506             PetscInt rStart = newPointOffsets[f][p];
7507             PetscInt b      = points[2 * p];
7508             PetscInt c, r, k;
7509             PetscInt dof;
7510 
7511             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7512             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7513               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7514               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7515 
7516               for (r = 0; r < nRows; r++) {
7517                 for (c = 0; c < newNumIndices; c++) {
7518                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7519                 }
7520               }
7521             } else {
7522               /* copy this row as is */
7523               for (r = 0; r < dof; r++) {
7524                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7525               }
7526             }
7527             oldOff += dof;
7528           }
7529         }
7530       } else {
7531         PetscInt oldOff = 0;
7532 
7533         for (p = 0; p < numPoints; p++) {
7534           PetscInt rStart = newPointOffsets[0][p];
7535           PetscInt b      = points[2 * p];
7536           PetscInt c, r, k;
7537           PetscInt dof;
7538 
7539           PetscCall(PetscSectionGetDof(section, b, &dof));
7540           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7541             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7542             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7543 
7544             for (r = 0; r < nRows; r++) {
7545               for (c = 0; c < newNumIndices; c++) {
7546                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7547               }
7548             }
7549           } else {
7550             /* copy this row as is */
7551             for (r = 0; r < dof; r++) {
7552               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7553             }
7554           }
7555           oldOff += dof;
7556         }
7557       }
7558 
7559       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7560     } else {
7561       newValues = tmpValues;
7562     }
7563   }
7564 
7565   /* clean up */
7566   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7567   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7568 
7569   if (numFields) {
7570     for (f = 0; f < numFields; f++) {
7571       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7572       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7573       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7574     }
7575   } else {
7576     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7577     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7578     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7579   }
7580   PetscCall(ISRestoreIndices(aIS, &anchors));
7581 
7582   /* output */
7583   if (outPoints) {
7584     *outPoints = newPoints;
7585   } else {
7586     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7587   }
7588   if (outValues) *outValues = newValues;
7589   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7590   PetscFunctionReturn(PETSC_SUCCESS);
7591 }
7592 
7593 /*@C
7594   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7595 
7596   Not collective
7597 
7598   Input Parameters:
7599 + dm         - The `DM`
7600 . section    - The `PetscSection` describing the points (a local section)
7601 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7602 . point      - The point defining the closure
7603 - useClPerm  - Use the closure point permutation if available
7604 
7605   Output Parameters:
7606 + numIndices - The number of dof indices in the closure of point with the input sections
7607 . indices    - The dof indices
7608 . outOffsets - Array to write the field offsets into, or `NULL`
7609 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7610 
7611   Level: advanced
7612 
7613   Notes:
7614   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7615 
7616   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7617   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7618   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7619   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7620   indices (with the above semantics) are implied.
7621 
7622 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7623           `PetscSection`, `DMGetGlobalSection()`
7624 @*/
7625 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7626 {
7627   /* Closure ordering */
7628   PetscSection    clSection;
7629   IS              clPoints;
7630   const PetscInt *clp;
7631   PetscInt       *points;
7632   const PetscInt *clperm = NULL;
7633   /* Dof permutation and sign flips */
7634   const PetscInt    **perms[32] = {NULL};
7635   const PetscScalar **flips[32] = {NULL};
7636   PetscScalar        *valCopy   = NULL;
7637   /* Hanging node constraints */
7638   PetscInt    *pointsC = NULL;
7639   PetscScalar *valuesC = NULL;
7640   PetscInt     NclC, NiC;
7641 
7642   PetscInt *idx;
7643   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7644   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7645 
7646   PetscFunctionBeginHot;
7647   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7648   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7649   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7650   if (numIndices) PetscAssertPointer(numIndices, 6);
7651   if (indices) PetscAssertPointer(indices, 7);
7652   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7653   if (values) PetscAssertPointer(values, 9);
7654   PetscCall(PetscSectionGetNumFields(section, &Nf));
7655   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7656   PetscCall(PetscArrayzero(offsets, 32));
7657   /* 1) Get points in closure */
7658   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7659   if (useClPerm) {
7660     PetscInt depth, clsize;
7661     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7662     for (clsize = 0, p = 0; p < Ncl; p++) {
7663       PetscInt dof;
7664       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7665       clsize += dof;
7666     }
7667     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7668   }
7669   /* 2) Get number of indices on these points and field offsets from section */
7670   for (p = 0; p < Ncl * 2; p += 2) {
7671     PetscInt dof, fdof;
7672 
7673     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7674     for (f = 0; f < Nf; ++f) {
7675       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7676       offsets[f + 1] += fdof;
7677     }
7678     Ni += dof;
7679   }
7680   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7681   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7682   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7683   for (f = 0; f < PetscMax(1, Nf); ++f) {
7684     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7685     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7686     /* may need to apply sign changes to the element matrix */
7687     if (values && flips[f]) {
7688       PetscInt foffset = offsets[f];
7689 
7690       for (p = 0; p < Ncl; ++p) {
7691         PetscInt           pnt  = points[2 * p], fdof;
7692         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7693 
7694         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7695         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7696         if (flip) {
7697           PetscInt i, j, k;
7698 
7699           if (!valCopy) {
7700             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7701             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7702             *values = valCopy;
7703           }
7704           for (i = 0; i < fdof; ++i) {
7705             PetscScalar fval = flip[i];
7706 
7707             for (k = 0; k < Ni; ++k) {
7708               valCopy[Ni * (foffset + i) + k] *= fval;
7709               valCopy[Ni * k + (foffset + i)] *= fval;
7710             }
7711           }
7712         }
7713         foffset += fdof;
7714       }
7715     }
7716   }
7717   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7718   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7719   if (NclC) {
7720     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7721     for (f = 0; f < PetscMax(1, Nf); ++f) {
7722       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7723       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7724     }
7725     for (f = 0; f < PetscMax(1, Nf); ++f) {
7726       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7727       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7728     }
7729     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7730     Ncl    = NclC;
7731     Ni     = NiC;
7732     points = pointsC;
7733     if (values) *values = valuesC;
7734   }
7735   /* 5) Calculate indices */
7736   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7737   if (Nf) {
7738     PetscInt  idxOff;
7739     PetscBool useFieldOffsets;
7740 
7741     if (outOffsets) {
7742       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7743     }
7744     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7745     if (useFieldOffsets) {
7746       for (p = 0; p < Ncl; ++p) {
7747         const PetscInt pnt = points[p * 2];
7748 
7749         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7750       }
7751     } else {
7752       for (p = 0; p < Ncl; ++p) {
7753         const PetscInt pnt = points[p * 2];
7754 
7755         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7756         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7757          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7758          * global section. */
7759         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7760       }
7761     }
7762   } else {
7763     PetscInt off = 0, idxOff;
7764 
7765     for (p = 0; p < Ncl; ++p) {
7766       const PetscInt  pnt  = points[p * 2];
7767       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7768 
7769       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7770       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7771        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7772       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7773     }
7774   }
7775   /* 6) Cleanup */
7776   for (f = 0; f < PetscMax(1, Nf); ++f) {
7777     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7778     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7779   }
7780   if (NclC) {
7781     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7782   } else {
7783     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7784   }
7785 
7786   if (numIndices) *numIndices = Ni;
7787   if (indices) *indices = idx;
7788   PetscFunctionReturn(PETSC_SUCCESS);
7789 }
7790 
7791 /*@C
7792   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7793 
7794   Not collective
7795 
7796   Input Parameters:
7797 + dm         - The `DM`
7798 . section    - The `PetscSection` describing the points (a local section)
7799 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7800 . point      - The point defining the closure
7801 - useClPerm  - Use the closure point permutation if available
7802 
7803   Output Parameters:
7804 + numIndices - The number of dof indices in the closure of point with the input sections
7805 . indices    - The dof indices
7806 . outOffsets - Array to write the field offsets into, or `NULL`
7807 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7808 
7809   Level: advanced
7810 
7811   Notes:
7812   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7813 
7814   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7815   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7816   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7817   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7818   indices (with the above semantics) are implied.
7819 
7820 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7821 @*/
7822 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7823 {
7824   PetscFunctionBegin;
7825   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7826   PetscAssertPointer(indices, 7);
7827   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7828   PetscFunctionReturn(PETSC_SUCCESS);
7829 }
7830 
7831 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7832 {
7833   DM_Plex           *mesh = (DM_Plex *)dm->data;
7834   PetscInt          *indices;
7835   PetscInt           numIndices;
7836   const PetscScalar *valuesOrig = values;
7837   PetscErrorCode     ierr;
7838 
7839   PetscFunctionBegin;
7840   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7841   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7842   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7843   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7844   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7845   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
7846 
7847   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
7848 
7849   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7850   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7851   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7852   if (ierr) {
7853     PetscMPIInt rank;
7854 
7855     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7856     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7857     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7858     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7859     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7860     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7861   }
7862   if (mesh->printFEM > 1) {
7863     PetscInt i;
7864     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7865     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7866     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7867   }
7868 
7869   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7870   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7871   PetscFunctionReturn(PETSC_SUCCESS);
7872 }
7873 
7874 /*@C
7875   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7876 
7877   Not collective
7878 
7879   Input Parameters:
7880 + dm            - The `DM`
7881 . section       - The section describing the layout in `v`, or `NULL` to use the default section
7882 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7883 . A             - The matrix
7884 . point         - The point in the `DM`
7885 . values        - The array of values
7886 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7887 
7888   Level: intermediate
7889 
7890 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7891 @*/
7892 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7893 {
7894   PetscFunctionBegin;
7895   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
7896   PetscFunctionReturn(PETSC_SUCCESS);
7897 }
7898 
7899 /*@C
7900   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
7901 
7902   Not collective
7903 
7904   Input Parameters:
7905 + dmRow            - The `DM` for the row fields
7906 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
7907 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
7908 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7909 . dmCol            - The `DM` for the column fields
7910 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
7911 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
7912 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7913 . A                - The matrix
7914 . point            - The point in the `DM`
7915 . values           - The array of values
7916 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7917 
7918   Level: intermediate
7919 
7920 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7921 @*/
7922 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, PetscBool useRowPerm, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, PetscBool useColPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7923 {
7924   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7925   PetscInt          *indicesRow, *indicesCol;
7926   PetscInt           numIndicesRow, numIndicesCol;
7927   const PetscScalar *valuesOrig = values;
7928   PetscErrorCode     ierr;
7929 
7930   PetscFunctionBegin;
7931   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7932   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7933   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7934   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7935   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7936   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
7937   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7938   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
7939   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7940   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
7941   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
7942 
7943   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7944   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7945 
7946   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7947   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7948   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7949   if (ierr) {
7950     PetscMPIInt rank;
7951 
7952     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7953     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7954     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7955     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7956     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7957     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7958   }
7959 
7960   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7961   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7962   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7963   PetscFunctionReturn(PETSC_SUCCESS);
7964 }
7965 
7966 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7967 {
7968   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7969   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7970   PetscInt       *cpoints = NULL;
7971   PetscInt       *findices, *cindices;
7972   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7973   PetscInt        foffsets[32], coffsets[32];
7974   DMPolytopeType  ct;
7975   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7976   PetscErrorCode  ierr;
7977 
7978   PetscFunctionBegin;
7979   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7980   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7981   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7982   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7983   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7984   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7985   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7986   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7987   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7988   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7989   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7990   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7991   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7992   PetscCall(PetscArrayzero(foffsets, 32));
7993   PetscCall(PetscArrayzero(coffsets, 32));
7994   /* Column indices */
7995   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7996   maxFPoints = numCPoints;
7997   /* Compress out points not in the section */
7998   /*   TODO: Squeeze out points with 0 dof as well */
7999   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8000   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8001     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8002       cpoints[q * 2]     = cpoints[p];
8003       cpoints[q * 2 + 1] = cpoints[p + 1];
8004       ++q;
8005     }
8006   }
8007   numCPoints = q;
8008   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8009     PetscInt fdof;
8010 
8011     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8012     if (!dof) continue;
8013     for (f = 0; f < numFields; ++f) {
8014       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8015       coffsets[f + 1] += fdof;
8016     }
8017     numCIndices += dof;
8018   }
8019   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8020   /* Row indices */
8021   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8022   {
8023     DMPlexTransform tr;
8024     DMPolytopeType *rct;
8025     PetscInt       *rsize, *rcone, *rornt, Nt;
8026 
8027     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8028     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8029     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8030     numSubcells = rsize[Nt - 1];
8031     PetscCall(DMPlexTransformDestroy(&tr));
8032   }
8033   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8034   for (r = 0, q = 0; r < numSubcells; ++r) {
8035     /* TODO Map from coarse to fine cells */
8036     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8037     /* Compress out points not in the section */
8038     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8039     for (p = 0; p < numFPoints * 2; p += 2) {
8040       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8041         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8042         if (!dof) continue;
8043         for (s = 0; s < q; ++s)
8044           if (fpoints[p] == ftotpoints[s * 2]) break;
8045         if (s < q) continue;
8046         ftotpoints[q * 2]     = fpoints[p];
8047         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8048         ++q;
8049       }
8050     }
8051     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8052   }
8053   numFPoints = q;
8054   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8055     PetscInt fdof;
8056 
8057     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8058     if (!dof) continue;
8059     for (f = 0; f < numFields; ++f) {
8060       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8061       foffsets[f + 1] += fdof;
8062     }
8063     numFIndices += dof;
8064   }
8065   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8066 
8067   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8068   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8069   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8070   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8071   if (numFields) {
8072     const PetscInt **permsF[32] = {NULL};
8073     const PetscInt **permsC[32] = {NULL};
8074 
8075     for (f = 0; f < numFields; f++) {
8076       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8077       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8078     }
8079     for (p = 0; p < numFPoints; p++) {
8080       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8081       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8082     }
8083     for (p = 0; p < numCPoints; p++) {
8084       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8085       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8086     }
8087     for (f = 0; f < numFields; f++) {
8088       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8089       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8090     }
8091   } else {
8092     const PetscInt **permsF = NULL;
8093     const PetscInt **permsC = NULL;
8094 
8095     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8096     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8097     for (p = 0, off = 0; p < numFPoints; p++) {
8098       const PetscInt *perm = permsF ? permsF[p] : NULL;
8099 
8100       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8101       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8102     }
8103     for (p = 0, off = 0; p < numCPoints; p++) {
8104       const PetscInt *perm = permsC ? permsC[p] : NULL;
8105 
8106       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8107       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8108     }
8109     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8110     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8111   }
8112   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8113   /* TODO: flips */
8114   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8115   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8116   if (ierr) {
8117     PetscMPIInt rank;
8118 
8119     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8120     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8121     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8122     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8123     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8124   }
8125   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8126   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8127   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8128   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8129   PetscFunctionReturn(PETSC_SUCCESS);
8130 }
8131 
8132 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8133 {
8134   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8135   PetscInt       *cpoints = NULL;
8136   PetscInt        foffsets[32], coffsets[32];
8137   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8138   DMPolytopeType  ct;
8139   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8140 
8141   PetscFunctionBegin;
8142   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8143   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8144   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8145   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8146   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8147   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8148   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8149   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8150   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8151   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8152   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8153   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8154   PetscCall(PetscArrayzero(foffsets, 32));
8155   PetscCall(PetscArrayzero(coffsets, 32));
8156   /* Column indices */
8157   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8158   maxFPoints = numCPoints;
8159   /* Compress out points not in the section */
8160   /*   TODO: Squeeze out points with 0 dof as well */
8161   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8162   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8163     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8164       cpoints[q * 2]     = cpoints[p];
8165       cpoints[q * 2 + 1] = cpoints[p + 1];
8166       ++q;
8167     }
8168   }
8169   numCPoints = q;
8170   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8171     PetscInt fdof;
8172 
8173     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8174     if (!dof) continue;
8175     for (f = 0; f < numFields; ++f) {
8176       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8177       coffsets[f + 1] += fdof;
8178     }
8179     numCIndices += dof;
8180   }
8181   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8182   /* Row indices */
8183   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8184   {
8185     DMPlexTransform tr;
8186     DMPolytopeType *rct;
8187     PetscInt       *rsize, *rcone, *rornt, Nt;
8188 
8189     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8190     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8191     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8192     numSubcells = rsize[Nt - 1];
8193     PetscCall(DMPlexTransformDestroy(&tr));
8194   }
8195   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8196   for (r = 0, q = 0; r < numSubcells; ++r) {
8197     /* TODO Map from coarse to fine cells */
8198     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8199     /* Compress out points not in the section */
8200     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8201     for (p = 0; p < numFPoints * 2; p += 2) {
8202       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8203         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8204         if (!dof) continue;
8205         for (s = 0; s < q; ++s)
8206           if (fpoints[p] == ftotpoints[s * 2]) break;
8207         if (s < q) continue;
8208         ftotpoints[q * 2]     = fpoints[p];
8209         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8210         ++q;
8211       }
8212     }
8213     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8214   }
8215   numFPoints = q;
8216   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8217     PetscInt fdof;
8218 
8219     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8220     if (!dof) continue;
8221     for (f = 0; f < numFields; ++f) {
8222       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8223       foffsets[f + 1] += fdof;
8224     }
8225     numFIndices += dof;
8226   }
8227   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8228 
8229   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8230   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8231   if (numFields) {
8232     const PetscInt **permsF[32] = {NULL};
8233     const PetscInt **permsC[32] = {NULL};
8234 
8235     for (f = 0; f < numFields; f++) {
8236       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8237       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8238     }
8239     for (p = 0; p < numFPoints; p++) {
8240       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8241       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8242     }
8243     for (p = 0; p < numCPoints; p++) {
8244       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8245       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8246     }
8247     for (f = 0; f < numFields; f++) {
8248       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8249       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8250     }
8251   } else {
8252     const PetscInt **permsF = NULL;
8253     const PetscInt **permsC = NULL;
8254 
8255     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8256     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8257     for (p = 0, off = 0; p < numFPoints; p++) {
8258       const PetscInt *perm = permsF ? permsF[p] : NULL;
8259 
8260       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8261       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8262     }
8263     for (p = 0, off = 0; p < numCPoints; p++) {
8264       const PetscInt *perm = permsC ? permsC[p] : NULL;
8265 
8266       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8267       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8268     }
8269     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8270     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8271   }
8272   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8273   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8274   PetscFunctionReturn(PETSC_SUCCESS);
8275 }
8276 
8277 /*@C
8278   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8279 
8280   Input Parameter:
8281 . dm - The `DMPLEX` object
8282 
8283   Output Parameter:
8284 . cellHeight - The height of a cell
8285 
8286   Level: developer
8287 
8288 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8289 @*/
8290 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8291 {
8292   DM_Plex *mesh = (DM_Plex *)dm->data;
8293 
8294   PetscFunctionBegin;
8295   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8296   PetscAssertPointer(cellHeight, 2);
8297   *cellHeight = mesh->vtkCellHeight;
8298   PetscFunctionReturn(PETSC_SUCCESS);
8299 }
8300 
8301 /*@C
8302   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8303 
8304   Input Parameters:
8305 + dm         - The `DMPLEX` object
8306 - cellHeight - The height of a cell
8307 
8308   Level: developer
8309 
8310 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8311 @*/
8312 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8313 {
8314   DM_Plex *mesh = (DM_Plex *)dm->data;
8315 
8316   PetscFunctionBegin;
8317   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8318   mesh->vtkCellHeight = cellHeight;
8319   PetscFunctionReturn(PETSC_SUCCESS);
8320 }
8321 
8322 /*@
8323   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8324 
8325   Input Parameters:
8326 + dm - The `DMPLEX` object
8327 - ct - The `DMPolytopeType` of the cell
8328 
8329   Output Parameters:
8330 + start - The first cell of this type, or `NULL`
8331 - end   - The upper bound on this celltype, or `NULL`
8332 
8333   Level: advanced
8334 
8335 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8336 @*/
8337 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8338 {
8339   DM_Plex *mesh = (DM_Plex *)dm->data;
8340   DMLabel  label;
8341   PetscInt pStart, pEnd;
8342 
8343   PetscFunctionBegin;
8344   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8345   if (start) {
8346     PetscAssertPointer(start, 3);
8347     *start = 0;
8348   }
8349   if (end) {
8350     PetscAssertPointer(end, 4);
8351     *end = 0;
8352   }
8353   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8354   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8355   if (mesh->tr) {
8356     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8357   } else {
8358     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8359     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8360     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8361   }
8362   PetscFunctionReturn(PETSC_SUCCESS);
8363 }
8364 
8365 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8366 {
8367   PetscSection section, globalSection;
8368   PetscInt    *numbers, p;
8369 
8370   PetscFunctionBegin;
8371   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8372   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8373   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8374   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8375   PetscCall(PetscSectionSetUp(section));
8376   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8377   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8378   for (p = pStart; p < pEnd; ++p) {
8379     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8380     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8381     else numbers[p - pStart] += shift;
8382   }
8383   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8384   if (globalSize) {
8385     PetscLayout layout;
8386     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8387     PetscCall(PetscLayoutGetSize(layout, globalSize));
8388     PetscCall(PetscLayoutDestroy(&layout));
8389   }
8390   PetscCall(PetscSectionDestroy(&section));
8391   PetscCall(PetscSectionDestroy(&globalSection));
8392   PetscFunctionReturn(PETSC_SUCCESS);
8393 }
8394 
8395 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8396 {
8397   PetscInt cellHeight, cStart, cEnd;
8398 
8399   PetscFunctionBegin;
8400   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8401   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8402   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8403   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8404   PetscFunctionReturn(PETSC_SUCCESS);
8405 }
8406 
8407 /*@
8408   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8409 
8410   Input Parameter:
8411 . dm - The `DMPLEX` object
8412 
8413   Output Parameter:
8414 . globalCellNumbers - Global cell numbers for all cells on this process
8415 
8416   Level: developer
8417 
8418 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8419 @*/
8420 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8421 {
8422   DM_Plex *mesh = (DM_Plex *)dm->data;
8423 
8424   PetscFunctionBegin;
8425   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8426   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8427   *globalCellNumbers = mesh->globalCellNumbers;
8428   PetscFunctionReturn(PETSC_SUCCESS);
8429 }
8430 
8431 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8432 {
8433   PetscInt vStart, vEnd;
8434 
8435   PetscFunctionBegin;
8436   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8437   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8438   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8439   PetscFunctionReturn(PETSC_SUCCESS);
8440 }
8441 
8442 /*@
8443   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8444 
8445   Input Parameter:
8446 . dm - The `DMPLEX` object
8447 
8448   Output Parameter:
8449 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8450 
8451   Level: developer
8452 
8453 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8454 @*/
8455 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8456 {
8457   DM_Plex *mesh = (DM_Plex *)dm->data;
8458 
8459   PetscFunctionBegin;
8460   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8461   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8462   *globalVertexNumbers = mesh->globalVertexNumbers;
8463   PetscFunctionReturn(PETSC_SUCCESS);
8464 }
8465 
8466 /*@
8467   DMPlexCreatePointNumbering - Create a global numbering for all points.
8468 
8469   Collective
8470 
8471   Input Parameter:
8472 . dm - The `DMPLEX` object
8473 
8474   Output Parameter:
8475 . globalPointNumbers - Global numbers for all points on this process
8476 
8477   Level: developer
8478 
8479   Notes:
8480   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8481   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8482   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8483   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8484 
8485   The partitioned mesh is
8486   ```
8487   (2)--0--(3)--1--(4)    (1)--0--(2)
8488   ```
8489   and its global numbering is
8490   ```
8491   (3)--0--(4)--1--(5)--2--(6)
8492   ```
8493   Then the global numbering is provided as
8494   ```
8495   [0] Number of indices in set 5
8496   [0] 0 0
8497   [0] 1 1
8498   [0] 2 3
8499   [0] 3 4
8500   [0] 4 -6
8501   [1] Number of indices in set 3
8502   [1] 0 2
8503   [1] 1 5
8504   [1] 2 6
8505   ```
8506 
8507 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8508 @*/
8509 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8510 {
8511   IS        nums[4];
8512   PetscInt  depths[4], gdepths[4], starts[4];
8513   PetscInt  depth, d, shift = 0;
8514   PetscBool empty = PETSC_FALSE;
8515 
8516   PetscFunctionBegin;
8517   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8518   PetscCall(DMPlexGetDepth(dm, &depth));
8519   // For unstratified meshes use dim instead of depth
8520   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8521   // If any stratum is empty, we must mark all empty
8522   for (d = 0; d <= depth; ++d) {
8523     PetscInt end;
8524 
8525     depths[d] = depth - d;
8526     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8527     if (!(starts[d] - end)) empty = PETSC_TRUE;
8528   }
8529   if (empty)
8530     for (d = 0; d <= depth; ++d) {
8531       depths[d] = -1;
8532       starts[d] = -1;
8533     }
8534   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8535   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8536   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]);
8537   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8538   for (d = 0; d <= depth; ++d) {
8539     PetscInt pStart, pEnd, gsize;
8540 
8541     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8542     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8543     shift += gsize;
8544   }
8545   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8546   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8547   PetscFunctionReturn(PETSC_SUCCESS);
8548 }
8549 
8550 /*@
8551   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8552 
8553   Input Parameter:
8554 . dm - The `DMPLEX` object
8555 
8556   Output Parameter:
8557 . ranks - The rank field
8558 
8559   Options Database Key:
8560 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8561 
8562   Level: intermediate
8563 
8564 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8565 @*/
8566 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8567 {
8568   DM             rdm;
8569   PetscFE        fe;
8570   PetscScalar   *r;
8571   PetscMPIInt    rank;
8572   DMPolytopeType ct;
8573   PetscInt       dim, cStart, cEnd, c;
8574   PetscBool      simplex;
8575 
8576   PetscFunctionBeginUser;
8577   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8578   PetscAssertPointer(ranks, 2);
8579   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8580   PetscCall(DMClone(dm, &rdm));
8581   PetscCall(DMGetDimension(rdm, &dim));
8582   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8583   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8584   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8585   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8586   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8587   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8588   PetscCall(PetscFEDestroy(&fe));
8589   PetscCall(DMCreateDS(rdm));
8590   PetscCall(DMCreateGlobalVector(rdm, ranks));
8591   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8592   PetscCall(VecGetArray(*ranks, &r));
8593   for (c = cStart; c < cEnd; ++c) {
8594     PetscScalar *lr;
8595 
8596     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8597     if (lr) *lr = rank;
8598   }
8599   PetscCall(VecRestoreArray(*ranks, &r));
8600   PetscCall(DMDestroy(&rdm));
8601   PetscFunctionReturn(PETSC_SUCCESS);
8602 }
8603 
8604 /*@
8605   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8606 
8607   Input Parameters:
8608 + dm    - The `DMPLEX`
8609 - label - The `DMLabel`
8610 
8611   Output Parameter:
8612 . val - The label value field
8613 
8614   Options Database Key:
8615 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8616 
8617   Level: intermediate
8618 
8619 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8620 @*/
8621 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8622 {
8623   DM           rdm;
8624   PetscFE      fe;
8625   PetscScalar *v;
8626   PetscInt     dim, cStart, cEnd, c;
8627 
8628   PetscFunctionBeginUser;
8629   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8630   PetscAssertPointer(label, 2);
8631   PetscAssertPointer(val, 3);
8632   PetscCall(DMClone(dm, &rdm));
8633   PetscCall(DMGetDimension(rdm, &dim));
8634   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8635   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8636   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8637   PetscCall(PetscFEDestroy(&fe));
8638   PetscCall(DMCreateDS(rdm));
8639   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8640   PetscCall(DMCreateGlobalVector(rdm, val));
8641   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8642   PetscCall(VecGetArray(*val, &v));
8643   for (c = cStart; c < cEnd; ++c) {
8644     PetscScalar *lv;
8645     PetscInt     cval;
8646 
8647     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8648     PetscCall(DMLabelGetValue(label, c, &cval));
8649     *lv = cval;
8650   }
8651   PetscCall(VecRestoreArray(*val, &v));
8652   PetscCall(DMDestroy(&rdm));
8653   PetscFunctionReturn(PETSC_SUCCESS);
8654 }
8655 
8656 /*@
8657   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8658 
8659   Input Parameter:
8660 . dm - The `DMPLEX` object
8661 
8662   Level: developer
8663 
8664   Notes:
8665   This is a useful diagnostic when creating meshes programmatically.
8666 
8667   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8668 
8669 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8670 @*/
8671 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8672 {
8673   PetscSection    coneSection, supportSection;
8674   const PetscInt *cone, *support;
8675   PetscInt        coneSize, c, supportSize, s;
8676   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8677   PetscBool       storagecheck = PETSC_TRUE;
8678 
8679   PetscFunctionBegin;
8680   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8681   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8682   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8683   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8684   /* Check that point p is found in the support of its cone points, and vice versa */
8685   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8686   for (p = pStart; p < pEnd; ++p) {
8687     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8688     PetscCall(DMPlexGetCone(dm, p, &cone));
8689     for (c = 0; c < coneSize; ++c) {
8690       PetscBool dup = PETSC_FALSE;
8691       PetscInt  d;
8692       for (d = c - 1; d >= 0; --d) {
8693         if (cone[c] == cone[d]) {
8694           dup = PETSC_TRUE;
8695           break;
8696         }
8697       }
8698       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8699       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8700       for (s = 0; s < supportSize; ++s) {
8701         if (support[s] == p) break;
8702       }
8703       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8704         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8705         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8706         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8707         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8708         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8709         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8710         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]);
8711         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8712       }
8713     }
8714     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8715     if (p != pp) {
8716       storagecheck = PETSC_FALSE;
8717       continue;
8718     }
8719     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8720     PetscCall(DMPlexGetSupport(dm, p, &support));
8721     for (s = 0; s < supportSize; ++s) {
8722       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8723       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8724       for (c = 0; c < coneSize; ++c) {
8725         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8726         if (cone[c] != pp) {
8727           c = 0;
8728           break;
8729         }
8730         if (cone[c] == p) break;
8731       }
8732       if (c >= coneSize) {
8733         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8734         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8735         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8736         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8737         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8738         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8739         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8740       }
8741     }
8742   }
8743   if (storagecheck) {
8744     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8745     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8746     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8747   }
8748   PetscFunctionReturn(PETSC_SUCCESS);
8749 }
8750 
8751 /*
8752   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.
8753 */
8754 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8755 {
8756   DMPolytopeType  cct;
8757   PetscInt        ptpoints[4];
8758   const PetscInt *cone, *ccone, *ptcone;
8759   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8760 
8761   PetscFunctionBegin;
8762   *unsplit = 0;
8763   switch (ct) {
8764   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8765     ptpoints[npt++] = c;
8766     break;
8767   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8768     PetscCall(DMPlexGetCone(dm, c, &cone));
8769     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8770     for (cp = 0; cp < coneSize; ++cp) {
8771       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8772       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8773     }
8774     break;
8775   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8776   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8777     PetscCall(DMPlexGetCone(dm, c, &cone));
8778     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8779     for (cp = 0; cp < coneSize; ++cp) {
8780       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8781       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8782       for (ccp = 0; ccp < cconeSize; ++ccp) {
8783         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8784         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8785           PetscInt p;
8786           for (p = 0; p < npt; ++p)
8787             if (ptpoints[p] == ccone[ccp]) break;
8788           if (p == npt) ptpoints[npt++] = ccone[ccp];
8789         }
8790       }
8791     }
8792     break;
8793   default:
8794     break;
8795   }
8796   for (pt = 0; pt < npt; ++pt) {
8797     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8798     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8799   }
8800   PetscFunctionReturn(PETSC_SUCCESS);
8801 }
8802 
8803 /*@
8804   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8805 
8806   Input Parameters:
8807 + dm         - The `DMPLEX` object
8808 - cellHeight - Normally 0
8809 
8810   Level: developer
8811 
8812   Notes:
8813   This is a useful diagnostic when creating meshes programmatically.
8814   Currently applicable only to homogeneous simplex or tensor meshes.
8815 
8816   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8817 
8818 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8819 @*/
8820 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8821 {
8822   DMPlexInterpolatedFlag interp;
8823   DMPolytopeType         ct;
8824   PetscInt               vStart, vEnd, cStart, cEnd, c;
8825 
8826   PetscFunctionBegin;
8827   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8828   PetscCall(DMPlexIsInterpolated(dm, &interp));
8829   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8830   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8831   for (c = cStart; c < cEnd; ++c) {
8832     PetscInt *closure = NULL;
8833     PetscInt  coneSize, closureSize, cl, Nv = 0;
8834 
8835     PetscCall(DMPlexGetCellType(dm, c, &ct));
8836     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8837     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8838     if (interp == DMPLEX_INTERPOLATED_FULL) {
8839       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8840       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));
8841     }
8842     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8843     for (cl = 0; cl < closureSize * 2; cl += 2) {
8844       const PetscInt p = closure[cl];
8845       if ((p >= vStart) && (p < vEnd)) ++Nv;
8846     }
8847     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8848     /* Special Case: Tensor faces with identified vertices */
8849     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8850       PetscInt unsplit;
8851 
8852       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8853       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8854     }
8855     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));
8856   }
8857   PetscFunctionReturn(PETSC_SUCCESS);
8858 }
8859 
8860 /*@
8861   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8862 
8863   Collective
8864 
8865   Input Parameters:
8866 + dm         - The `DMPLEX` object
8867 - cellHeight - Normally 0
8868 
8869   Level: developer
8870 
8871   Notes:
8872   This is a useful diagnostic when creating meshes programmatically.
8873   This routine is only relevant for meshes that are fully interpolated across all ranks.
8874   It will error out if a partially interpolated mesh is given on some rank.
8875   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8876 
8877   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8878 
8879 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8880 @*/
8881 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8882 {
8883   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8884   DMPlexInterpolatedFlag interpEnum;
8885 
8886   PetscFunctionBegin;
8887   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8888   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8889   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8890   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8891     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8892     PetscFunctionReturn(PETSC_SUCCESS);
8893   }
8894 
8895   PetscCall(DMGetDimension(dm, &dim));
8896   PetscCall(DMPlexGetDepth(dm, &depth));
8897   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8898   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8899     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8900     for (c = cStart; c < cEnd; ++c) {
8901       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8902       const DMPolytopeType *faceTypes;
8903       DMPolytopeType        ct;
8904       PetscInt              numFaces, coneSize, f;
8905       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8906 
8907       PetscCall(DMPlexGetCellType(dm, c, &ct));
8908       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8909       if (unsplit) continue;
8910       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8911       PetscCall(DMPlexGetCone(dm, c, &cone));
8912       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8913       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8914       for (cl = 0; cl < closureSize * 2; cl += 2) {
8915         const PetscInt p = closure[cl];
8916         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8917       }
8918       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8919       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);
8920       for (f = 0; f < numFaces; ++f) {
8921         DMPolytopeType fct;
8922         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8923 
8924         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8925         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8926         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8927           const PetscInt p = fclosure[cl];
8928           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8929         }
8930         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]);
8931         for (v = 0; v < fnumCorners; ++v) {
8932           if (fclosure[v] != faces[fOff + v]) {
8933             PetscInt v1;
8934 
8935             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8936             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8937             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8938             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8939             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8940             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]);
8941           }
8942         }
8943         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8944         fOff += faceSizes[f];
8945       }
8946       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8947       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8948     }
8949   }
8950   PetscFunctionReturn(PETSC_SUCCESS);
8951 }
8952 
8953 /*@
8954   DMPlexCheckGeometry - Check the geometry of mesh cells
8955 
8956   Input Parameter:
8957 . dm - The `DMPLEX` object
8958 
8959   Level: developer
8960 
8961   Notes:
8962   This is a useful diagnostic when creating meshes programmatically.
8963 
8964   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8965 
8966 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8967 @*/
8968 PetscErrorCode DMPlexCheckGeometry(DM dm)
8969 {
8970   Vec       coordinates;
8971   PetscReal detJ, J[9], refVol = 1.0;
8972   PetscReal vol;
8973   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8974 
8975   PetscFunctionBegin;
8976   PetscCall(DMGetDimension(dm, &dim));
8977   PetscCall(DMGetCoordinateDim(dm, &dE));
8978   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
8979   PetscCall(DMPlexGetDepth(dm, &depth));
8980   for (d = 0; d < dim; ++d) refVol *= 2.0;
8981   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8982   /* Make sure local coordinates are created, because that step is collective */
8983   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8984   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
8985   for (c = cStart; c < cEnd; ++c) {
8986     DMPolytopeType ct;
8987     PetscInt       unsplit;
8988     PetscBool      ignoreZeroVol = PETSC_FALSE;
8989 
8990     PetscCall(DMPlexGetCellType(dm, c, &ct));
8991     switch (ct) {
8992     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8993     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8994     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8995       ignoreZeroVol = PETSC_TRUE;
8996       break;
8997     default:
8998       break;
8999     }
9000     switch (ct) {
9001     case DM_POLYTOPE_TRI_PRISM:
9002     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9003     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9004     case DM_POLYTOPE_PYRAMID:
9005       continue;
9006     default:
9007       break;
9008     }
9009     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9010     if (unsplit) continue;
9011     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9012     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);
9013     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9014     /* This should work with periodicity since DG coordinates should be used */
9015     if (depth > 1) {
9016       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9017       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);
9018       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9019     }
9020   }
9021   PetscFunctionReturn(PETSC_SUCCESS);
9022 }
9023 
9024 /*@
9025   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9026 
9027   Collective
9028 
9029   Input Parameters:
9030 + dm              - The `DMPLEX` object
9031 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9032 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9033 
9034   Level: developer
9035 
9036   Notes:
9037   This is mainly intended for debugging/testing purposes.
9038 
9039   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9040 
9041   Extra roots can come from priodic cuts, where additional points appear on the boundary
9042 
9043 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9044 @*/
9045 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9046 {
9047   PetscInt           l, nleaves, nroots, overlap;
9048   const PetscInt    *locals;
9049   const PetscSFNode *remotes;
9050   PetscBool          distributed;
9051   MPI_Comm           comm;
9052   PetscMPIInt        rank;
9053 
9054   PetscFunctionBegin;
9055   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9056   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9057   else pointSF = dm->sf;
9058   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9059   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9060   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9061   {
9062     PetscMPIInt mpiFlag;
9063 
9064     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9065     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9066   }
9067   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9068   PetscCall(DMPlexIsDistributed(dm, &distributed));
9069   if (!distributed) {
9070     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);
9071     PetscFunctionReturn(PETSC_SUCCESS);
9072   }
9073   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);
9074   PetscCall(DMPlexGetOverlap(dm, &overlap));
9075 
9076   /* Check SF graph is compatible with DMPlex chart */
9077   {
9078     PetscInt pStart, pEnd, maxLeaf;
9079 
9080     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9081     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9082     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9083     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9084   }
9085 
9086   /* Check Point SF has no local points referenced */
9087   for (l = 0; l < nleaves; l++) {
9088     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);
9089   }
9090 
9091   /* Check there are no cells in interface */
9092   if (!overlap) {
9093     PetscInt cellHeight, cStart, cEnd;
9094 
9095     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9096     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9097     for (l = 0; l < nleaves; ++l) {
9098       const PetscInt point = locals ? locals[l] : l;
9099 
9100       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9101     }
9102   }
9103 
9104   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9105   {
9106     const PetscInt *rootdegree;
9107 
9108     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9109     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9110     for (l = 0; l < nleaves; ++l) {
9111       const PetscInt  point = locals ? locals[l] : l;
9112       const PetscInt *cone;
9113       PetscInt        coneSize, c, idx;
9114 
9115       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9116       PetscCall(DMPlexGetCone(dm, point, &cone));
9117       for (c = 0; c < coneSize; ++c) {
9118         if (!rootdegree[cone[c]]) {
9119           if (locals) {
9120             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9121           } else {
9122             idx = (cone[c] < nleaves) ? cone[c] : -1;
9123           }
9124           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9125         }
9126       }
9127     }
9128   }
9129   PetscFunctionReturn(PETSC_SUCCESS);
9130 }
9131 
9132 /*@
9133   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9134 
9135   Input Parameter:
9136 . dm - The `DMPLEX` object
9137 
9138   Level: developer
9139 
9140   Notes:
9141   This is a useful diagnostic when creating meshes programmatically.
9142 
9143   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9144 
9145   Currently does not include `DMPlexCheckCellShape()`.
9146 
9147 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9148 @*/
9149 PetscErrorCode DMPlexCheck(DM dm)
9150 {
9151   PetscInt cellHeight;
9152 
9153   PetscFunctionBegin;
9154   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9155   PetscCall(DMPlexCheckSymmetry(dm));
9156   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9157   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9158   PetscCall(DMPlexCheckGeometry(dm));
9159   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9160   PetscCall(DMPlexCheckInterfaceCones(dm));
9161   PetscFunctionReturn(PETSC_SUCCESS);
9162 }
9163 
9164 typedef struct cell_stats {
9165   PetscReal min, max, sum, squaresum;
9166   PetscInt  count;
9167 } cell_stats_t;
9168 
9169 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9170 {
9171   PetscInt i, N = *len;
9172 
9173   for (i = 0; i < N; i++) {
9174     cell_stats_t *A = (cell_stats_t *)a;
9175     cell_stats_t *B = (cell_stats_t *)b;
9176 
9177     B->min = PetscMin(A->min, B->min);
9178     B->max = PetscMax(A->max, B->max);
9179     B->sum += A->sum;
9180     B->squaresum += A->squaresum;
9181     B->count += A->count;
9182   }
9183 }
9184 
9185 /*@
9186   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9187 
9188   Collective
9189 
9190   Input Parameters:
9191 + dm        - The `DMPLEX` object
9192 . output    - If true, statistics will be displayed on `stdout`
9193 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9194 
9195   Level: developer
9196 
9197   Notes:
9198   This is mainly intended for debugging/testing purposes.
9199 
9200   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9201 
9202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9203 @*/
9204 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9205 {
9206   DM           dmCoarse;
9207   cell_stats_t stats, globalStats;
9208   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9209   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9210   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9211   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9212   PetscMPIInt  rank, size;
9213 
9214   PetscFunctionBegin;
9215   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9216   stats.min = PETSC_MAX_REAL;
9217   stats.max = PETSC_MIN_REAL;
9218   stats.sum = stats.squaresum = 0.;
9219   stats.count                 = 0;
9220 
9221   PetscCallMPI(MPI_Comm_size(comm, &size));
9222   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9223   PetscCall(DMGetCoordinateDim(dm, &cdim));
9224   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9225   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9226   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9227   for (c = cStart; c < cEnd; c++) {
9228     PetscInt  i;
9229     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9230 
9231     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9232     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9233     for (i = 0; i < PetscSqr(cdim); ++i) {
9234       frobJ += J[i] * J[i];
9235       frobInvJ += invJ[i] * invJ[i];
9236     }
9237     cond2 = frobJ * frobInvJ;
9238     cond  = PetscSqrtReal(cond2);
9239 
9240     stats.min = PetscMin(stats.min, cond);
9241     stats.max = PetscMax(stats.max, cond);
9242     stats.sum += cond;
9243     stats.squaresum += cond2;
9244     stats.count++;
9245     if (output && cond > limit) {
9246       PetscSection coordSection;
9247       Vec          coordsLocal;
9248       PetscScalar *coords = NULL;
9249       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9250 
9251       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9252       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9253       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9254       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9255       for (i = 0; i < Nv / cdim; ++i) {
9256         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9257         for (d = 0; d < cdim; ++d) {
9258           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9259           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9260         }
9261         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9262       }
9263       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9264       for (cl = 0; cl < clSize * 2; cl += 2) {
9265         const PetscInt edge = closure[cl];
9266 
9267         if ((edge >= eStart) && (edge < eEnd)) {
9268           PetscReal len;
9269 
9270           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9271           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9272         }
9273       }
9274       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9275       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9276     }
9277   }
9278   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9279 
9280   if (size > 1) {
9281     PetscMPIInt  blockLengths[2] = {4, 1};
9282     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9283     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9284     MPI_Op       statReduce;
9285 
9286     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9287     PetscCallMPI(MPI_Type_commit(&statType));
9288     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9289     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9290     PetscCallMPI(MPI_Op_free(&statReduce));
9291     PetscCallMPI(MPI_Type_free(&statType));
9292   } else {
9293     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9294   }
9295   if (rank == 0) {
9296     count = globalStats.count;
9297     min   = globalStats.min;
9298     max   = globalStats.max;
9299     mean  = globalStats.sum / globalStats.count;
9300     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9301   }
9302 
9303   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));
9304   PetscCall(PetscFree2(J, invJ));
9305 
9306   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9307   if (dmCoarse) {
9308     PetscBool isplex;
9309 
9310     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9311     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9312   }
9313   PetscFunctionReturn(PETSC_SUCCESS);
9314 }
9315 
9316 /*@
9317   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9318   orthogonal quality below given tolerance.
9319 
9320   Collective
9321 
9322   Input Parameters:
9323 + dm   - The `DMPLEX` object
9324 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9325 - atol - [0, 1] Absolute tolerance for tagging cells.
9326 
9327   Output Parameters:
9328 + OrthQual      - `Vec` containing orthogonal quality per cell
9329 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9330 
9331   Options Database Keys:
9332 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9333 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9334 
9335   Level: intermediate
9336 
9337   Notes:
9338   Orthogonal quality is given by the following formula\:
9339 
9340   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9341 
9342   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
9343   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9344   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9345   calculating the cosine of the angle between these vectors.
9346 
9347   Orthogonal quality ranges from 1 (best) to 0 (worst).
9348 
9349   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9350   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9351 
9352   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9353 
9354 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9355 @*/
9356 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9357 {
9358   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9359   PetscInt              *idx;
9360   PetscScalar           *oqVals;
9361   const PetscScalar     *cellGeomArr, *faceGeomArr;
9362   PetscReal             *ci, *fi, *Ai;
9363   MPI_Comm               comm;
9364   Vec                    cellgeom, facegeom;
9365   DM                     dmFace, dmCell;
9366   IS                     glob;
9367   ISLocalToGlobalMapping ltog;
9368   PetscViewer            vwr;
9369 
9370   PetscFunctionBegin;
9371   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9372   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9373   PetscAssertPointer(OrthQual, 4);
9374   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9375   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9376   PetscCall(DMGetDimension(dm, &nc));
9377   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9378   {
9379     DMPlexInterpolatedFlag interpFlag;
9380 
9381     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9382     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9383       PetscMPIInt rank;
9384 
9385       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9386       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9387     }
9388   }
9389   if (OrthQualLabel) {
9390     PetscAssertPointer(OrthQualLabel, 5);
9391     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9392     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9393   } else {
9394     *OrthQualLabel = NULL;
9395   }
9396   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9397   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9398   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9399   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9400   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9401   PetscCall(VecCreate(comm, OrthQual));
9402   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9403   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9404   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9405   PetscCall(VecSetUp(*OrthQual));
9406   PetscCall(ISDestroy(&glob));
9407   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9408   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9409   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9410   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9411   PetscCall(VecGetDM(cellgeom, &dmCell));
9412   PetscCall(VecGetDM(facegeom, &dmFace));
9413   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9414   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9415     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9416     PetscInt         cellarr[2], *adj = NULL;
9417     PetscScalar     *cArr, *fArr;
9418     PetscReal        minvalc = 1.0, minvalf = 1.0;
9419     PetscFVCellGeom *cg;
9420 
9421     idx[cellIter] = cell - cStart;
9422     cellarr[0]    = cell;
9423     /* Make indexing into cellGeom easier */
9424     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9425     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9426     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9427     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9428     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9429       PetscInt         i;
9430       const PetscInt   neigh  = adj[cellneigh];
9431       PetscReal        normci = 0, normfi = 0, normai = 0;
9432       PetscFVCellGeom *cgneigh;
9433       PetscFVFaceGeom *fg;
9434 
9435       /* Don't count ourselves in the neighbor list */
9436       if (neigh == cell) continue;
9437       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9438       cellarr[1] = neigh;
9439       {
9440         PetscInt        numcovpts;
9441         const PetscInt *covpts;
9442 
9443         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9444         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9445         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9446       }
9447 
9448       /* Compute c_i, f_i and their norms */
9449       for (i = 0; i < nc; i++) {
9450         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9451         fi[i] = fg->centroid[i] - cg->centroid[i];
9452         Ai[i] = fg->normal[i];
9453         normci += PetscPowReal(ci[i], 2);
9454         normfi += PetscPowReal(fi[i], 2);
9455         normai += PetscPowReal(Ai[i], 2);
9456       }
9457       normci = PetscSqrtReal(normci);
9458       normfi = PetscSqrtReal(normfi);
9459       normai = PetscSqrtReal(normai);
9460 
9461       /* Normalize and compute for each face-cell-normal pair */
9462       for (i = 0; i < nc; i++) {
9463         ci[i] = ci[i] / normci;
9464         fi[i] = fi[i] / normfi;
9465         Ai[i] = Ai[i] / normai;
9466         /* PetscAbs because I don't know if normals are guaranteed to point out */
9467         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9468         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9469       }
9470       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9471       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9472     }
9473     PetscCall(PetscFree(adj));
9474     PetscCall(PetscFree2(cArr, fArr));
9475     /* Defer to cell if they're equal */
9476     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9477     if (OrthQualLabel) {
9478       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9479     }
9480   }
9481   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9482   PetscCall(VecAssemblyBegin(*OrthQual));
9483   PetscCall(VecAssemblyEnd(*OrthQual));
9484   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9485   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9486   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9487   if (OrthQualLabel) {
9488     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9489   }
9490   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9491   PetscCall(PetscViewerDestroy(&vwr));
9492   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9493   PetscFunctionReturn(PETSC_SUCCESS);
9494 }
9495 
9496 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9497  * interpolator construction */
9498 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9499 {
9500   PetscSection section, newSection, gsection;
9501   PetscSF      sf;
9502   PetscBool    hasConstraints, ghasConstraints;
9503 
9504   PetscFunctionBegin;
9505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9506   PetscAssertPointer(odm, 2);
9507   PetscCall(DMGetLocalSection(dm, &section));
9508   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9509   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9510   if (!ghasConstraints) {
9511     PetscCall(PetscObjectReference((PetscObject)dm));
9512     *odm = dm;
9513     PetscFunctionReturn(PETSC_SUCCESS);
9514   }
9515   PetscCall(DMClone(dm, odm));
9516   PetscCall(DMCopyFields(dm, *odm));
9517   PetscCall(DMGetLocalSection(*odm, &newSection));
9518   PetscCall(DMGetPointSF(*odm, &sf));
9519   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9520   PetscCall(DMSetGlobalSection(*odm, gsection));
9521   PetscCall(PetscSectionDestroy(&gsection));
9522   PetscFunctionReturn(PETSC_SUCCESS);
9523 }
9524 
9525 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9526 {
9527   DM        dmco, dmfo;
9528   Mat       interpo;
9529   Vec       rscale;
9530   Vec       cglobalo, clocal;
9531   Vec       fglobal, fglobalo, flocal;
9532   PetscBool regular;
9533 
9534   PetscFunctionBegin;
9535   PetscCall(DMGetFullDM(dmc, &dmco));
9536   PetscCall(DMGetFullDM(dmf, &dmfo));
9537   PetscCall(DMSetCoarseDM(dmfo, dmco));
9538   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9539   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9540   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9541   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9542   PetscCall(DMCreateLocalVector(dmc, &clocal));
9543   PetscCall(VecSet(cglobalo, 0.));
9544   PetscCall(VecSet(clocal, 0.));
9545   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9546   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9547   PetscCall(DMCreateLocalVector(dmf, &flocal));
9548   PetscCall(VecSet(fglobal, 0.));
9549   PetscCall(VecSet(fglobalo, 0.));
9550   PetscCall(VecSet(flocal, 0.));
9551   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9552   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9553   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9554   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9555   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9556   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9557   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9558   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9559   *shift = fglobal;
9560   PetscCall(VecDestroy(&flocal));
9561   PetscCall(VecDestroy(&fglobalo));
9562   PetscCall(VecDestroy(&clocal));
9563   PetscCall(VecDestroy(&cglobalo));
9564   PetscCall(VecDestroy(&rscale));
9565   PetscCall(MatDestroy(&interpo));
9566   PetscCall(DMDestroy(&dmfo));
9567   PetscCall(DMDestroy(&dmco));
9568   PetscFunctionReturn(PETSC_SUCCESS);
9569 }
9570 
9571 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9572 {
9573   PetscObject shifto;
9574   Vec         shift;
9575 
9576   PetscFunctionBegin;
9577   if (!interp) {
9578     Vec rscale;
9579 
9580     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9581     PetscCall(VecDestroy(&rscale));
9582   } else {
9583     PetscCall(PetscObjectReference((PetscObject)interp));
9584   }
9585   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9586   if (!shifto) {
9587     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9588     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9589     shifto = (PetscObject)shift;
9590     PetscCall(VecDestroy(&shift));
9591   }
9592   shift = (Vec)shifto;
9593   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9594   PetscCall(VecAXPY(fineSol, 1.0, shift));
9595   PetscCall(MatDestroy(&interp));
9596   PetscFunctionReturn(PETSC_SUCCESS);
9597 }
9598 
9599 /* Pointwise interpolation
9600      Just code FEM for now
9601      u^f = I u^c
9602      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9603      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9604      I_{ij} = psi^f_i phi^c_j
9605 */
9606 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9607 {
9608   PetscSection gsc, gsf;
9609   PetscInt     m, n;
9610   void        *ctx;
9611   DM           cdm;
9612   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9613 
9614   PetscFunctionBegin;
9615   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9616   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9617   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9618   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9619 
9620   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9621   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9622   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9623   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9624   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9625 
9626   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9627   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9628   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9629   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9630   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9631   if (scaling) {
9632     /* Use naive scaling */
9633     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9634   }
9635   PetscFunctionReturn(PETSC_SUCCESS);
9636 }
9637 
9638 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9639 {
9640   VecScatter ctx;
9641 
9642   PetscFunctionBegin;
9643   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9644   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9645   PetscCall(VecScatterDestroy(&ctx));
9646   PetscFunctionReturn(PETSC_SUCCESS);
9647 }
9648 
9649 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[])
9650 {
9651   const PetscInt Nc = uOff[1] - uOff[0];
9652   PetscInt       c;
9653   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9654 }
9655 
9656 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9657 {
9658   DM           dmc;
9659   PetscDS      ds;
9660   Vec          ones, locmass;
9661   IS           cellIS;
9662   PetscFormKey key;
9663   PetscInt     depth;
9664 
9665   PetscFunctionBegin;
9666   PetscCall(DMClone(dm, &dmc));
9667   PetscCall(DMCopyDisc(dm, dmc));
9668   PetscCall(DMGetDS(dmc, &ds));
9669   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9670   PetscCall(DMCreateGlobalVector(dmc, mass));
9671   PetscCall(DMGetLocalVector(dmc, &ones));
9672   PetscCall(DMGetLocalVector(dmc, &locmass));
9673   PetscCall(DMPlexGetDepth(dmc, &depth));
9674   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9675   PetscCall(VecSet(locmass, 0.0));
9676   PetscCall(VecSet(ones, 1.0));
9677   key.label = NULL;
9678   key.value = 0;
9679   key.field = 0;
9680   key.part  = 0;
9681   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9682   PetscCall(ISDestroy(&cellIS));
9683   PetscCall(VecSet(*mass, 0.0));
9684   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9685   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9686   PetscCall(DMRestoreLocalVector(dmc, &ones));
9687   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9688   PetscCall(DMDestroy(&dmc));
9689   PetscFunctionReturn(PETSC_SUCCESS);
9690 }
9691 
9692 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9693 {
9694   PetscSection gsc, gsf;
9695   PetscInt     m, n;
9696   void        *ctx;
9697   DM           cdm;
9698   PetscBool    regular;
9699 
9700   PetscFunctionBegin;
9701   if (dmFine == dmCoarse) {
9702     DM            dmc;
9703     PetscDS       ds;
9704     PetscWeakForm wf;
9705     Vec           u;
9706     IS            cellIS;
9707     PetscFormKey  key;
9708     PetscInt      depth;
9709 
9710     PetscCall(DMClone(dmFine, &dmc));
9711     PetscCall(DMCopyDisc(dmFine, dmc));
9712     PetscCall(DMGetDS(dmc, &ds));
9713     PetscCall(PetscDSGetWeakForm(ds, &wf));
9714     PetscCall(PetscWeakFormClear(wf));
9715     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9716     PetscCall(DMCreateMatrix(dmc, mass));
9717     PetscCall(DMGetLocalVector(dmc, &u));
9718     PetscCall(DMPlexGetDepth(dmc, &depth));
9719     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9720     PetscCall(MatZeroEntries(*mass));
9721     key.label = NULL;
9722     key.value = 0;
9723     key.field = 0;
9724     key.part  = 0;
9725     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9726     PetscCall(ISDestroy(&cellIS));
9727     PetscCall(DMRestoreLocalVector(dmc, &u));
9728     PetscCall(DMDestroy(&dmc));
9729   } else {
9730     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9731     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9732     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9733     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9734 
9735     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9736     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9737     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9738     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9739 
9740     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9741     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9742     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9743     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9744   }
9745   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9746   PetscFunctionReturn(PETSC_SUCCESS);
9747 }
9748 
9749 /*@
9750   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9751 
9752   Input Parameter:
9753 . dm - The `DMPLEX` object
9754 
9755   Output Parameter:
9756 . regular - The flag
9757 
9758   Level: intermediate
9759 
9760 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9761 @*/
9762 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9763 {
9764   PetscFunctionBegin;
9765   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9766   PetscAssertPointer(regular, 2);
9767   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9768   PetscFunctionReturn(PETSC_SUCCESS);
9769 }
9770 
9771 /*@
9772   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9773 
9774   Input Parameters:
9775 + dm      - The `DMPLEX` object
9776 - regular - The flag
9777 
9778   Level: intermediate
9779 
9780 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9781 @*/
9782 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9783 {
9784   PetscFunctionBegin;
9785   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9786   ((DM_Plex *)dm->data)->regularRefinement = regular;
9787   PetscFunctionReturn(PETSC_SUCCESS);
9788 }
9789 
9790 /*@
9791   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9792   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9793 
9794   Not Collective
9795 
9796   Input Parameter:
9797 . dm - The `DMPLEX` object
9798 
9799   Output Parameters:
9800 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9801 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9802 
9803   Level: intermediate
9804 
9805 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9806 @*/
9807 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9808 {
9809   DM_Plex *plex = (DM_Plex *)dm->data;
9810 
9811   PetscFunctionBegin;
9812   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9813   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9814   if (anchorSection) *anchorSection = plex->anchorSection;
9815   if (anchorIS) *anchorIS = plex->anchorIS;
9816   PetscFunctionReturn(PETSC_SUCCESS);
9817 }
9818 
9819 /*@
9820   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
9821 
9822   Collective
9823 
9824   Input Parameters:
9825 + dm            - The `DMPLEX` object
9826 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9827                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9828 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9829 
9830   Level: intermediate
9831 
9832   Notes:
9833   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
9834   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
9835   combination of other points' degrees of freedom.
9836 
9837   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9838   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9839 
9840   The reference counts of `anchorSection` and `anchorIS` are incremented.
9841 
9842 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9843 @*/
9844 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9845 {
9846   DM_Plex    *plex = (DM_Plex *)dm->data;
9847   PetscMPIInt result;
9848 
9849   PetscFunctionBegin;
9850   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9851   if (anchorSection) {
9852     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9853     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9854     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9855   }
9856   if (anchorIS) {
9857     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9858     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9859     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9860   }
9861 
9862   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9863   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9864   plex->anchorSection = anchorSection;
9865 
9866   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9867   PetscCall(ISDestroy(&plex->anchorIS));
9868   plex->anchorIS = anchorIS;
9869 
9870   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9871     PetscInt        size, a, pStart, pEnd;
9872     const PetscInt *anchors;
9873 
9874     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9875     PetscCall(ISGetLocalSize(anchorIS, &size));
9876     PetscCall(ISGetIndices(anchorIS, &anchors));
9877     for (a = 0; a < size; a++) {
9878       PetscInt p;
9879 
9880       p = anchors[a];
9881       if (p >= pStart && p < pEnd) {
9882         PetscInt dof;
9883 
9884         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9885         if (dof) {
9886           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9887           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9888         }
9889       }
9890     }
9891     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9892   }
9893   /* reset the generic constraints */
9894   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9895   PetscFunctionReturn(PETSC_SUCCESS);
9896 }
9897 
9898 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9899 {
9900   PetscSection anchorSection;
9901   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9902 
9903   PetscFunctionBegin;
9904   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9905   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9906   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9907   PetscCall(PetscSectionGetNumFields(section, &numFields));
9908   if (numFields) {
9909     PetscInt f;
9910     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9911 
9912     for (f = 0; f < numFields; f++) {
9913       PetscInt numComp;
9914 
9915       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9916       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9917     }
9918   }
9919   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9920   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9921   pStart = PetscMax(pStart, sStart);
9922   pEnd   = PetscMin(pEnd, sEnd);
9923   pEnd   = PetscMax(pStart, pEnd);
9924   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9925   for (p = pStart; p < pEnd; p++) {
9926     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9927     if (dof) {
9928       PetscCall(PetscSectionGetDof(section, p, &dof));
9929       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9930       for (f = 0; f < numFields; f++) {
9931         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9932         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9933       }
9934     }
9935   }
9936   PetscCall(PetscSectionSetUp(*cSec));
9937   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9938   PetscFunctionReturn(PETSC_SUCCESS);
9939 }
9940 
9941 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9942 {
9943   PetscSection    aSec;
9944   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9945   const PetscInt *anchors;
9946   PetscInt        numFields, f;
9947   IS              aIS;
9948   MatType         mtype;
9949   PetscBool       iscuda, iskokkos;
9950 
9951   PetscFunctionBegin;
9952   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9953   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9954   PetscCall(PetscSectionGetStorageSize(section, &n));
9955   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9956   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9957   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9958   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9959   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9960   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9961   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9962   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9963   else mtype = MATSEQAIJ;
9964   PetscCall(MatSetType(*cMat, mtype));
9965   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9966   PetscCall(ISGetIndices(aIS, &anchors));
9967   /* cSec will be a subset of aSec and section */
9968   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9969   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9970   PetscCall(PetscMalloc1(m + 1, &i));
9971   i[0] = 0;
9972   PetscCall(PetscSectionGetNumFields(section, &numFields));
9973   for (p = pStart; p < pEnd; p++) {
9974     PetscInt rDof, rOff, r;
9975 
9976     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9977     if (!rDof) continue;
9978     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9979     if (numFields) {
9980       for (f = 0; f < numFields; f++) {
9981         annz = 0;
9982         for (r = 0; r < rDof; r++) {
9983           a = anchors[rOff + r];
9984           if (a < sStart || a >= sEnd) continue;
9985           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9986           annz += aDof;
9987         }
9988         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9989         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9990         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9991       }
9992     } else {
9993       annz = 0;
9994       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9995       for (q = 0; q < dof; q++) {
9996         a = anchors[rOff + q];
9997         if (a < sStart || a >= sEnd) continue;
9998         PetscCall(PetscSectionGetDof(section, a, &aDof));
9999         annz += aDof;
10000       }
10001       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10002       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10003       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10004     }
10005   }
10006   nnz = i[m];
10007   PetscCall(PetscMalloc1(nnz, &j));
10008   offset = 0;
10009   for (p = pStart; p < pEnd; p++) {
10010     if (numFields) {
10011       for (f = 0; f < numFields; f++) {
10012         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10013         for (q = 0; q < dof; q++) {
10014           PetscInt rDof, rOff, r;
10015           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10016           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10017           for (r = 0; r < rDof; r++) {
10018             PetscInt s;
10019 
10020             a = anchors[rOff + r];
10021             if (a < sStart || a >= sEnd) continue;
10022             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10023             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10024             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10025           }
10026         }
10027       }
10028     } else {
10029       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10030       for (q = 0; q < dof; q++) {
10031         PetscInt rDof, rOff, r;
10032         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10033         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10034         for (r = 0; r < rDof; r++) {
10035           PetscInt s;
10036 
10037           a = anchors[rOff + r];
10038           if (a < sStart || a >= sEnd) continue;
10039           PetscCall(PetscSectionGetDof(section, a, &aDof));
10040           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10041           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10042         }
10043       }
10044     }
10045   }
10046   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10047   PetscCall(PetscFree(i));
10048   PetscCall(PetscFree(j));
10049   PetscCall(ISRestoreIndices(aIS, &anchors));
10050   PetscFunctionReturn(PETSC_SUCCESS);
10051 }
10052 
10053 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10054 {
10055   DM_Plex     *plex = (DM_Plex *)dm->data;
10056   PetscSection anchorSection, section, cSec;
10057   Mat          cMat;
10058 
10059   PetscFunctionBegin;
10060   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10061   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10062   if (anchorSection) {
10063     PetscInt Nf;
10064 
10065     PetscCall(DMGetLocalSection(dm, &section));
10066     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10067     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10068     PetscCall(DMGetNumFields(dm, &Nf));
10069     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10070     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10071     PetscCall(PetscSectionDestroy(&cSec));
10072     PetscCall(MatDestroy(&cMat));
10073   }
10074   PetscFunctionReturn(PETSC_SUCCESS);
10075 }
10076 
10077 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10078 {
10079   IS           subis;
10080   PetscSection section, subsection;
10081 
10082   PetscFunctionBegin;
10083   PetscCall(DMGetLocalSection(dm, &section));
10084   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10085   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10086   /* Create subdomain */
10087   PetscCall(DMPlexFilter(dm, label, value, subdm));
10088   /* Create submodel */
10089   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10090   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10091   PetscCall(DMSetLocalSection(*subdm, subsection));
10092   PetscCall(PetscSectionDestroy(&subsection));
10093   PetscCall(DMCopyDisc(dm, *subdm));
10094   /* Create map from submodel to global model */
10095   if (is) {
10096     PetscSection    sectionGlobal, subsectionGlobal;
10097     IS              spIS;
10098     const PetscInt *spmap;
10099     PetscInt       *subIndices;
10100     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10101     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10102 
10103     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10104     PetscCall(ISGetIndices(spIS, &spmap));
10105     PetscCall(PetscSectionGetNumFields(section, &Nf));
10106     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10107     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10108     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10109     for (p = pStart; p < pEnd; ++p) {
10110       PetscInt gdof, pSubSize = 0;
10111 
10112       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10113       if (gdof > 0) {
10114         for (f = 0; f < Nf; ++f) {
10115           PetscInt fdof, fcdof;
10116 
10117           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10118           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10119           pSubSize += fdof - fcdof;
10120         }
10121         subSize += pSubSize;
10122         if (pSubSize) {
10123           if (bs < 0) {
10124             bs = pSubSize;
10125           } else if (bs != pSubSize) {
10126             /* Layout does not admit a pointwise block size */
10127             bs = 1;
10128           }
10129         }
10130       }
10131     }
10132     /* Must have same blocksize on all procs (some might have no points) */
10133     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10134     bsLocal[1] = bs;
10135     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10136     if (bsMinMax[0] != bsMinMax[1]) {
10137       bs = 1;
10138     } else {
10139       bs = bsMinMax[0];
10140     }
10141     PetscCall(PetscMalloc1(subSize, &subIndices));
10142     for (p = pStart; p < pEnd; ++p) {
10143       PetscInt gdof, goff;
10144 
10145       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10146       if (gdof > 0) {
10147         const PetscInt point = spmap[p];
10148 
10149         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10150         for (f = 0; f < Nf; ++f) {
10151           PetscInt fdof, fcdof, fc, f2, poff = 0;
10152 
10153           /* Can get rid of this loop by storing field information in the global section */
10154           for (f2 = 0; f2 < f; ++f2) {
10155             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10156             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10157             poff += fdof - fcdof;
10158           }
10159           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10160           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10161           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10162         }
10163       }
10164     }
10165     PetscCall(ISRestoreIndices(spIS, &spmap));
10166     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10167     if (bs > 1) {
10168       /* We need to check that the block size does not come from non-contiguous fields */
10169       PetscInt i, j, set = 1;
10170       for (i = 0; i < subSize; i += bs) {
10171         for (j = 0; j < bs; ++j) {
10172           if (subIndices[i + j] != subIndices[i] + j) {
10173             set = 0;
10174             break;
10175           }
10176         }
10177       }
10178       if (set) PetscCall(ISSetBlockSize(*is, bs));
10179     }
10180     /* Attach nullspace */
10181     for (f = 0; f < Nf; ++f) {
10182       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10183       if ((*subdm)->nullspaceConstructors[f]) break;
10184     }
10185     if (f < Nf) {
10186       MatNullSpace nullSpace;
10187       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10188 
10189       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10190       PetscCall(MatNullSpaceDestroy(&nullSpace));
10191     }
10192   }
10193   PetscFunctionReturn(PETSC_SUCCESS);
10194 }
10195 
10196 /*@
10197   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10198 
10199   Input Parameters:
10200 + dm    - The `DM`
10201 - dummy - unused argument
10202 
10203   Options Database Key:
10204 . -dm_plex_monitor_throughput - Activate the monitor
10205 
10206   Level: developer
10207 
10208 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10209 @*/
10210 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10211 {
10212   PetscLogHandler default_handler;
10213 
10214   PetscFunctionBegin;
10215   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10216   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10217   if (default_handler) {
10218     PetscLogEvent      event;
10219     PetscEventPerfInfo eventInfo;
10220     PetscReal          cellRate, flopRate;
10221     PetscInt           cStart, cEnd, Nf, N;
10222     const char        *name;
10223 
10224     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10225     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10226     PetscCall(DMGetNumFields(dm, &Nf));
10227     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10228     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10229     N        = (cEnd - cStart) * Nf * eventInfo.count;
10230     flopRate = eventInfo.flops / eventInfo.time;
10231     cellRate = N / eventInfo.time;
10232     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)));
10233   } else {
10234     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off or the default log handler is not running. Reconfigure using --with-log and run with -log_view.");
10235   }
10236   PetscFunctionReturn(PETSC_SUCCESS);
10237 }
10238