xref: /petsc/src/dm/impls/plex/plex.c (revision fff3f5fae336d1caaec33911beb15ba4efbb662b)
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;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The `DMPLEX` object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(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: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(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   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(PETSC_SUCCESS);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective on dm
154 
155   Input Parameters:
156 + dm - The `DMPLEX` object
157 . n  - The number of vectors
158 . u  - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
202         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
203         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(PETSC_SUCCESS);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *array;
253   PetscReal          lbound[3], ubound[3];
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
278   PetscCall(PetscDrawClear(draw));
279 
280   /* Could implement something like DMDASelectFields() */
281   for (f = 0; f < Nf; ++f) {
282     DM          fdm = dm;
283     Vec         fv  = v;
284     IS          fis;
285     char        prefix[PETSC_MAX_PATH_LEN];
286     const char *fname;
287 
288     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
289     PetscCall(PetscSectionGetFieldName(s, f, &fname));
290 
291     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
292     else prefix[0] = '\0';
293     if (Nf > 1) {
294       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
295       PetscCall(VecGetSubVector(v, fis, &fv));
296       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
297       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
298     }
299     for (comp = 0; comp < Nc; ++comp, ++w) {
300       PetscInt nmax = 2;
301 
302       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
303       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
304       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
305       PetscCall(PetscDrawSetTitle(draw, title));
306 
307       /* TODO Get max and min only for this component */
308       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
309       if (!flg) {
310         PetscCall(VecMin(fv, NULL, &vbound[0]));
311         PetscCall(VecMax(fv, NULL, &vbound[1]));
312         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
313       }
314 
315       PetscCall(PetscDrawGetPopup(draw, &popup));
316       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
317       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
318       PetscCall(VecGetArrayRead(fv, &array));
319       for (c = cStart; c < cEnd; ++c) {
320         PetscScalar       *coords = NULL, *a = NULL;
321         const PetscScalar *coords_arr;
322         PetscBool          isDG;
323         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
324 
325         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
326         if (a) {
327           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
328           color[1] = color[2] = color[3] = color[0];
329         } else {
330           PetscScalar *vals = NULL;
331           PetscInt     numVals, va;
332 
333           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
334           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
335           switch (numVals / Nc) {
336           case 3: /* P1 Triangle */
337           case 4: /* P1 Quadrangle */
338             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
339             break;
340           case 6: /* P2 Triangle */
341           case 8: /* P2 Quadrangle */
342             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
343             break;
344           default:
345             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
346           }
347           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
348         }
349         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
350         switch (numCoords) {
351         case 6:
352         case 12: /* Localized triangle */
353           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
354           break;
355         case 8:
356         case 16: /* Localized quadrilateral */
357           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
358           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
359           break;
360         default:
361           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
362         }
363         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
364       }
365       PetscCall(VecRestoreArrayRead(fv, &array));
366       PetscCall(PetscDrawFlush(draw));
367       PetscCall(PetscDrawPause(draw));
368       PetscCall(PetscDrawSave(draw));
369     }
370     if (Nf > 1) {
371       PetscCall(VecRestoreSubVector(v, fis, &fv));
372       PetscCall(ISDestroy(&fis));
373       PetscCall(DMDestroy(&fdm));
374     }
375   }
376   PetscFunctionReturn(PETSC_SUCCESS);
377 }
378 
379 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
380 {
381   DM        dm;
382   PetscDraw draw;
383   PetscInt  dim;
384   PetscBool isnull;
385 
386   PetscFunctionBegin;
387   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
388   PetscCall(PetscDrawIsNull(draw, &isnull));
389   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
390 
391   PetscCall(VecGetDM(v, &dm));
392   PetscCall(DMGetCoordinateDim(dm, &dim));
393   switch (dim) {
394   case 1:
395     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
396     break;
397   case 2:
398     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
399     break;
400   default:
401     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
402   }
403   PetscFunctionReturn(PETSC_SUCCESS);
404 }
405 
406 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
407 {
408   DM                      dm;
409   Vec                     locv;
410   const char             *name;
411   PetscSection            section;
412   PetscInt                pStart, pEnd;
413   PetscInt                numFields;
414   PetscViewerVTKFieldType ft;
415 
416   PetscFunctionBegin;
417   PetscCall(VecGetDM(v, &dm));
418   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
419   PetscCall(PetscObjectGetName((PetscObject)v, &name));
420   PetscCall(PetscObjectSetName((PetscObject)locv, name));
421   PetscCall(VecCopy(v, locv));
422   PetscCall(DMGetLocalSection(dm, &section));
423   PetscCall(PetscSectionGetNumFields(section, &numFields));
424   if (!numFields) {
425     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
426     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
427   } else {
428     PetscInt f;
429 
430     for (f = 0; f < numFields; f++) {
431       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
432       if (ft == PETSC_VTK_INVALID) continue;
433       PetscCall(PetscObjectReference((PetscObject)locv));
434       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
435     }
436     PetscCall(VecDestroy(&locv));
437   }
438   PetscFunctionReturn(PETSC_SUCCESS);
439 }
440 
441 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
442 {
443   DM        dm;
444   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
445 
446   PetscFunctionBegin;
447   PetscCall(VecGetDM(v, &dm));
448   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
449   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
450   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
453   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
454   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
455     PetscInt    i, numFields;
456     PetscObject fe;
457     PetscBool   fem  = PETSC_FALSE;
458     Vec         locv = v;
459     const char *name;
460     PetscInt    step;
461     PetscReal   time;
462 
463     PetscCall(DMGetNumFields(dm, &numFields));
464     for (i = 0; i < numFields; i++) {
465       PetscCall(DMGetField(dm, i, NULL, &fe));
466       if (fe->classid == PETSCFE_CLASSID) {
467         fem = PETSC_TRUE;
468         break;
469       }
470     }
471     if (fem) {
472       PetscObject isZero;
473 
474       PetscCall(DMGetLocalVector(dm, &locv));
475       PetscCall(PetscObjectGetName((PetscObject)v, &name));
476       PetscCall(PetscObjectSetName((PetscObject)locv, name));
477       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
478       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
479       PetscCall(VecCopy(v, locv));
480       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
481       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
482     }
483     if (isvtk) {
484       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
485     } else if (ishdf5) {
486 #if defined(PETSC_HAVE_HDF5)
487       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
488 #else
489       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
490 #endif
491     } else if (isdraw) {
492       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
493     } else if (isglvis) {
494       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
495       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
496       PetscCall(VecView_GLVis(locv, viewer));
497     } else if (iscgns) {
498 #if defined(PETSC_HAVE_CGNS)
499       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
500 #else
501       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
502 #endif
503     }
504     if (fem) {
505       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
506       PetscCall(DMRestoreLocalVector(dm, &locv));
507     }
508   } else {
509     PetscBool isseq;
510 
511     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
512     if (isseq) PetscCall(VecView_Seq(v, viewer));
513     else PetscCall(VecView_MPI(v, viewer));
514   }
515   PetscFunctionReturn(PETSC_SUCCESS);
516 }
517 
518 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
519 {
520   DM        dm;
521   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
527   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
528   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
529   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
532   if (isvtk || isdraw || isglvis || iscgns) {
533     Vec         locv;
534     PetscObject isZero;
535     const char *name;
536 
537     PetscCall(DMGetLocalVector(dm, &locv));
538     PetscCall(PetscObjectGetName((PetscObject)v, &name));
539     PetscCall(PetscObjectSetName((PetscObject)locv, name));
540     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
541     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
542     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
543     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
544     PetscCall(VecView_Plex_Local(locv, viewer));
545     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
546     PetscCall(DMRestoreLocalVector(dm, &locv));
547   } else if (ishdf5) {
548 #if defined(PETSC_HAVE_HDF5)
549     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
550 #else
551     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
552 #endif
553   } else if (isexodusii) {
554 #if defined(PETSC_HAVE_EXODUSII)
555     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
556 #else
557     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
558 #endif
559   } else {
560     PetscBool isseq;
561 
562     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
563     if (isseq) PetscCall(VecView_Seq(v, viewer));
564     else PetscCall(VecView_MPI(v, viewer));
565   }
566   PetscFunctionReturn(PETSC_SUCCESS);
567 }
568 
569 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
570 {
571   DM                dm;
572   MPI_Comm          comm;
573   PetscViewerFormat format;
574   Vec               v;
575   PetscBool         isvtk, ishdf5;
576 
577   PetscFunctionBegin;
578   PetscCall(VecGetDM(originalv, &dm));
579   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
580   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
581   PetscCall(PetscViewerGetFormat(viewer, &format));
582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
583   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
584   if (format == PETSC_VIEWER_NATIVE) {
585     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
586     /* this need a better fix */
587     if (dm->useNatural) {
588       if (dm->sfNatural) {
589         const char *vecname;
590         PetscInt    n, nroots;
591 
592         PetscCall(VecGetLocalSize(originalv, &n));
593         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
594         if (n == nroots) {
595           PetscCall(DMPlexCreateNaturalVector(dm, &v));
596           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
597           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
598           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
599           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
600         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
601       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
602     } else v = originalv;
603   } else v = originalv;
604 
605   if (ishdf5) {
606 #if defined(PETSC_HAVE_HDF5)
607     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
608 #else
609     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
610 #endif
611   } else if (isvtk) {
612     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
613   } else {
614     PetscBool isseq;
615 
616     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
617     if (isseq) PetscCall(VecView_Seq(v, viewer));
618     else PetscCall(VecView_MPI(v, viewer));
619   }
620   if (v != originalv) PetscCall(VecDestroy(&v));
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
625 {
626   DM        dm;
627   PetscBool ishdf5;
628 
629   PetscFunctionBegin;
630   PetscCall(VecGetDM(v, &dm));
631   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
633   if (ishdf5) {
634     DM          dmBC;
635     Vec         gv;
636     const char *name;
637 
638     PetscCall(DMGetOutputDM(dm, &dmBC));
639     PetscCall(DMGetGlobalVector(dmBC, &gv));
640     PetscCall(PetscObjectGetName((PetscObject)v, &name));
641     PetscCall(PetscObjectSetName((PetscObject)gv, name));
642     PetscCall(VecLoad_Default(gv, viewer));
643     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
644     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
645     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
646   } else PetscCall(VecLoad_Default(v, viewer));
647   PetscFunctionReturn(PETSC_SUCCESS);
648 }
649 
650 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
651 {
652   DM        dm;
653   PetscBool ishdf5, isexodusii;
654 
655   PetscFunctionBegin;
656   PetscCall(VecGetDM(v, &dm));
657   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
659   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
660   if (ishdf5) {
661 #if defined(PETSC_HAVE_HDF5)
662     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
665 #endif
666   } else if (isexodusii) {
667 #if defined(PETSC_HAVE_EXODUSII)
668     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
669 #else
670     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
671 #endif
672   } else PetscCall(VecLoad_Default(v, viewer));
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   PetscViewerFormat format;
680   PetscBool         ishdf5;
681 
682   PetscFunctionBegin;
683   PetscCall(VecGetDM(originalv, &dm));
684   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
685   PetscCall(PetscViewerGetFormat(viewer, &format));
686   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
687   if (format == PETSC_VIEWER_NATIVE) {
688     if (dm->useNatural) {
689       if (dm->sfNatural) {
690         if (ishdf5) {
691 #if defined(PETSC_HAVE_HDF5)
692           Vec         v;
693           const char *vecname;
694 
695           PetscCall(DMPlexCreateNaturalVector(dm, &v));
696           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
697           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
698           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
699           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
700           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
701           PetscCall(VecDestroy(&v));
702 #else
703           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
704 #endif
705         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
706       }
707     } else PetscCall(VecLoad_Default(originalv, viewer));
708   }
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
713 {
714   PetscSection       coordSection;
715   Vec                coordinates;
716   DMLabel            depthLabel, celltypeLabel;
717   const char        *name[4];
718   const PetscScalar *a;
719   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
720 
721   PetscFunctionBegin;
722   PetscCall(DMGetDimension(dm, &dim));
723   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
724   PetscCall(DMGetCoordinateSection(dm, &coordSection));
725   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
726   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
727   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
728   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
729   PetscCall(VecGetArrayRead(coordinates, &a));
730   name[0]       = "vertex";
731   name[1]       = "edge";
732   name[dim - 1] = "face";
733   name[dim]     = "cell";
734   for (c = cStart; c < cEnd; ++c) {
735     PetscInt *closure = NULL;
736     PetscInt  closureSize, cl, ct;
737 
738     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
739     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
740     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
741     PetscCall(PetscViewerASCIIPushTab(viewer));
742     for (cl = 0; cl < closureSize * 2; cl += 2) {
743       PetscInt point = closure[cl], depth, dof, off, d, p;
744 
745       if ((point < pStart) || (point >= pEnd)) continue;
746       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
747       if (!dof) continue;
748       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
749       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
750       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
751       for (p = 0; p < dof / dim; ++p) {
752         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
753         for (d = 0; d < dim; ++d) {
754           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
755           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
756         }
757         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
758       }
759       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
760     }
761     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
762     PetscCall(PetscViewerASCIIPopTab(viewer));
763   }
764   PetscCall(VecRestoreArrayRead(coordinates, &a));
765   PetscFunctionReturn(PETSC_SUCCESS);
766 }
767 
768 typedef enum {
769   CS_CARTESIAN,
770   CS_POLAR,
771   CS_CYLINDRICAL,
772   CS_SPHERICAL
773 } CoordSystem;
774 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
775 
776 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
777 {
778   PetscInt i;
779 
780   PetscFunctionBegin;
781   if (dim > 3) {
782     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
783   } else {
784     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
785 
786     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
787     switch (cs) {
788     case CS_CARTESIAN:
789       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
790       break;
791     case CS_POLAR:
792       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
793       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
794       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
795       break;
796     case CS_CYLINDRICAL:
797       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       trcoords[2] = coords[2];
801       break;
802     case CS_SPHERICAL:
803       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
804       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
805       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
806       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
807       break;
808     }
809     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
810   }
811   PetscFunctionReturn(PETSC_SUCCESS);
812 }
813 
814 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
815 {
816   DM_Plex          *mesh = (DM_Plex *)dm->data;
817   DM                cdm, cdmCell;
818   PetscSection      coordSection, coordSectionCell;
819   Vec               coordinates, coordinatesCell;
820   PetscViewerFormat format;
821 
822   PetscFunctionBegin;
823   PetscCall(PetscViewerGetFormat(viewer, &format));
824   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
825     const char *name;
826     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
827     PetscInt    pStart, pEnd, p, numLabels, l;
828     PetscMPIInt rank, size;
829 
830     PetscCall(DMGetCoordinateDM(dm, &cdm));
831     PetscCall(DMGetCoordinateSection(dm, &coordSection));
832     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
833     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
834     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
835     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
836     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
837     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
838     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
839     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
840     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
841     PetscCall(DMGetDimension(dm, &dim));
842     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
843     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
844     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
845     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
847     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
848     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
849     for (p = pStart; p < pEnd; ++p) {
850       PetscInt dof, off, s;
851 
852       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
853       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
854       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
855     }
856     PetscCall(PetscViewerFlush(viewer));
857     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
858     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
859     for (p = pStart; p < pEnd; ++p) {
860       PetscInt dof, off, c;
861 
862       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
863       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
864       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
865     }
866     PetscCall(PetscViewerFlush(viewer));
867     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
868     if (coordSection && coordinates) {
869       CoordSystem        cs = CS_CARTESIAN;
870       const PetscScalar *array, *arrayCell = NULL;
871       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
872       PetscMPIInt        rank;
873       const char        *name;
874 
875       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
876       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
877       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
878       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
879       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
880       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
881       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
882       pStart = PetscMin(pvStart, pcStart);
883       pEnd   = PetscMax(pvEnd, pcEnd);
884       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
885       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
886       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
887       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
888 
889       PetscCall(VecGetArrayRead(coordinates, &array));
890       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
891       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
892       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
893       for (p = pStart; p < pEnd; ++p) {
894         PetscInt dof, off;
895 
896         if (p >= pvStart && p < pvEnd) {
897           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
898           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
899           if (dof) {
900             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
901             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
902             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
903           }
904         }
905         if (cdmCell && p >= pcStart && p < pcEnd) {
906           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
907           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
908           if (dof) {
909             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
910             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
911             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
912           }
913         }
914       }
915       PetscCall(PetscViewerFlush(viewer));
916       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
917       PetscCall(VecRestoreArrayRead(coordinates, &array));
918       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
919     }
920     PetscCall(DMGetNumLabels(dm, &numLabels));
921     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
922     for (l = 0; l < numLabels; ++l) {
923       DMLabel     label;
924       PetscBool   isdepth;
925       const char *name;
926 
927       PetscCall(DMGetLabelName(dm, l, &name));
928       PetscCall(PetscStrcmp(name, "depth", &isdepth));
929       if (isdepth) continue;
930       PetscCall(DMGetLabel(dm, name, &label));
931       PetscCall(DMLabelView(label, viewer));
932     }
933     if (size > 1) {
934       PetscSF sf;
935 
936       PetscCall(DMGetPointSF(dm, &sf));
937       PetscCall(PetscSFView(sf, viewer));
938     }
939     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
940     PetscCall(PetscViewerFlush(viewer));
941   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
942     const char  *name, *color;
943     const char  *defcolors[3]  = {"gray", "orange", "green"};
944     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
945     char         lname[PETSC_MAX_PATH_LEN];
946     PetscReal    scale      = 2.0;
947     PetscReal    tikzscale  = 1.0;
948     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
949     double       tcoords[3];
950     PetscScalar *coords;
951     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
952     PetscMPIInt  rank, size;
953     char       **names, **colors, **lcolors;
954     PetscBool    flg, lflg;
955     PetscBT      wp = NULL;
956     PetscInt     pEnd, pStart;
957 
958     PetscCall(DMGetCoordinateDM(dm, &cdm));
959     PetscCall(DMGetCoordinateSection(dm, &coordSection));
960     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
961     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
962     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
963     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
964     PetscCall(DMGetDimension(dm, &dim));
965     PetscCall(DMPlexGetDepth(dm, &depth));
966     PetscCall(DMGetNumLabels(dm, &numLabels));
967     numLabels  = PetscMax(numLabels, 10);
968     numColors  = 10;
969     numLColors = 10;
970     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
971     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
972     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
973     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
974     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
975     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
976     n = 4;
977     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
978     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
979     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
980     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
981     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
982     if (!useLabels) numLabels = 0;
983     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
984     if (!useColors) {
985       numColors = 3;
986       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
987     }
988     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
989     if (!useColors) {
990       numLColors = 4;
991       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
992     }
993     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
994     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
995     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
996     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
997     if (depth < dim) plotEdges = PETSC_FALSE;
998     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
999 
1000     /* filter points with labelvalue != labeldefaultvalue */
1001     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1002     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1003     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1004     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1005     if (lflg) {
1006       DMLabel lbl;
1007 
1008       PetscCall(DMGetLabel(dm, lname, &lbl));
1009       if (lbl) {
1010         PetscInt val, defval;
1011 
1012         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1013         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1014         for (c = pStart; c < pEnd; c++) {
1015           PetscInt *closure = NULL;
1016           PetscInt  closureSize;
1017 
1018           PetscCall(DMLabelGetValue(lbl, c, &val));
1019           if (val == defval) continue;
1020 
1021           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1022           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1023           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1024         }
1025       }
1026     }
1027 
1028     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1029     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1030     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1031     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1032 \\documentclass[tikz]{standalone}\n\n\
1033 \\usepackage{pgflibraryshapes}\n\
1034 \\usetikzlibrary{backgrounds}\n\
1035 \\usetikzlibrary{arrows}\n\
1036 \\begin{document}\n"));
1037     if (size > 1) {
1038       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1039       for (p = 0; p < size; ++p) {
1040         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1041         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1042       }
1043       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1044     }
1045     if (drawHasse) {
1046       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1047 
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1060     }
1061     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1062 
1063     /* Plot vertices */
1064     PetscCall(VecGetArray(coordinates, &coords));
1065     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1066     for (v = vStart; v < vEnd; ++v) {
1067       PetscInt  off, dof, d;
1068       PetscBool isLabeled = PETSC_FALSE;
1069 
1070       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1071       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1072       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1073       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1074       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1075       for (d = 0; d < dof; ++d) {
1076         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1077         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1078       }
1079       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1080       if (dim == 3) {
1081         PetscReal tmp = tcoords[1];
1082         tcoords[1]    = tcoords[2];
1083         tcoords[2]    = -tmp;
1084       }
1085       for (d = 0; d < dof; ++d) {
1086         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1088       }
1089       if (drawHasse) color = colors[0 % numColors];
1090       else color = colors[rank % numColors];
1091       for (l = 0; l < numLabels; ++l) {
1092         PetscInt val;
1093         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1094         if (val >= 0) {
1095           color     = lcolors[l % numLColors];
1096           isLabeled = PETSC_TRUE;
1097           break;
1098         }
1099       }
1100       if (drawNumbers[0]) {
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1102       } else if (drawColors[0]) {
1103         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1104       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1105     }
1106     PetscCall(VecRestoreArray(coordinates, &coords));
1107     PetscCall(PetscViewerFlush(viewer));
1108     /* Plot edges */
1109     if (plotEdges) {
1110       PetscCall(VecGetArray(coordinates, &coords));
1111       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1112       for (e = eStart; e < eEnd; ++e) {
1113         const PetscInt *cone;
1114         PetscInt        coneSize, offA, offB, dof, d;
1115 
1116         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1117         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1118         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1119         PetscCall(DMPlexGetCone(dm, e, &cone));
1120         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1121         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1122         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1123         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1124         for (d = 0; d < dof; ++d) {
1125           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1126           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1127         }
1128         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1129         if (dim == 3) {
1130           PetscReal tmp = tcoords[1];
1131           tcoords[1]    = tcoords[2];
1132           tcoords[2]    = -tmp;
1133         }
1134         for (d = 0; d < dof; ++d) {
1135           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1136           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1137         }
1138         if (drawHasse) color = colors[1 % numColors];
1139         else color = colors[rank % numColors];
1140         for (l = 0; l < numLabels; ++l) {
1141           PetscInt val;
1142           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1143           if (val >= 0) {
1144             color = lcolors[l % numLColors];
1145             break;
1146           }
1147         }
1148         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1149       }
1150       PetscCall(VecRestoreArray(coordinates, &coords));
1151       PetscCall(PetscViewerFlush(viewer));
1152       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1153     }
1154     /* Plot cells */
1155     if (dim == 3 || !drawNumbers[1]) {
1156       for (e = eStart; e < eEnd; ++e) {
1157         const PetscInt *cone;
1158 
1159         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1160         color = colors[rank % numColors];
1161         for (l = 0; l < numLabels; ++l) {
1162           PetscInt val;
1163           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1164           if (val >= 0) {
1165             color = lcolors[l % numLColors];
1166             break;
1167           }
1168         }
1169         PetscCall(DMPlexGetCone(dm, e, &cone));
1170         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1171       }
1172     } else {
1173       DMPolytopeType ct;
1174 
1175       /* Drawing a 2D polygon */
1176       for (c = cStart; c < cEnd; ++c) {
1177         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1178         PetscCall(DMPlexGetCellType(dm, c, &ct));
1179         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1180           const PetscInt *cone;
1181           PetscInt        coneSize, e;
1182 
1183           PetscCall(DMPlexGetCone(dm, c, &cone));
1184           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1185           for (e = 0; e < coneSize; ++e) {
1186             const PetscInt *econe;
1187 
1188             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1189             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1190           }
1191         } else {
1192           PetscInt *closure = NULL;
1193           PetscInt  closureSize, Nv = 0, v;
1194 
1195           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1196           for (p = 0; p < closureSize * 2; p += 2) {
1197             const PetscInt point = closure[p];
1198 
1199             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1200           }
1201           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1202           for (v = 0; v <= Nv; ++v) {
1203             const PetscInt vertex = closure[v % Nv];
1204 
1205             if (v > 0) {
1206               if (plotEdges) {
1207                 const PetscInt *edge;
1208                 PetscInt        endpoints[2], ne;
1209 
1210                 endpoints[0] = closure[v - 1];
1211                 endpoints[1] = vertex;
1212                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1213                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1214                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1215                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1216               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1217             }
1218             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1219           }
1220           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1221           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1222         }
1223       }
1224     }
1225     for (c = cStart; c < cEnd; ++c) {
1226       double             ccoords[3] = {0.0, 0.0, 0.0};
1227       PetscBool          isLabeled  = PETSC_FALSE;
1228       PetscScalar       *cellCoords = NULL;
1229       const PetscScalar *array;
1230       PetscInt           numCoords, cdim, d;
1231       PetscBool          isDG;
1232 
1233       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1234       PetscCall(DMGetCoordinateDim(dm, &cdim));
1235       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1236       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1237       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1238       for (p = 0; p < numCoords / cdim; ++p) {
1239         for (d = 0; d < cdim; ++d) {
1240           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1241           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1242         }
1243         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1244         if (cdim == 3) {
1245           PetscReal tmp = tcoords[1];
1246           tcoords[1]    = tcoords[2];
1247           tcoords[2]    = -tmp;
1248         }
1249         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1250       }
1251       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1252       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1253       for (d = 0; d < cdim; ++d) {
1254         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1255         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1256       }
1257       if (drawHasse) color = colors[depth % numColors];
1258       else color = colors[rank % numColors];
1259       for (l = 0; l < numLabels; ++l) {
1260         PetscInt val;
1261         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1262         if (val >= 0) {
1263           color     = lcolors[l % numLColors];
1264           isLabeled = PETSC_TRUE;
1265           break;
1266         }
1267       }
1268       if (drawNumbers[dim]) {
1269         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1270       } else if (drawColors[dim]) {
1271         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1272       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1273     }
1274     if (drawHasse) {
1275       color = colors[depth % numColors];
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1277       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1279       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1281 
1282       color = colors[1 % numColors];
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1286       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1288 
1289       color = colors[0 % numColors];
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1295 
1296       for (p = pStart; p < pEnd; ++p) {
1297         const PetscInt *cone;
1298         PetscInt        coneSize, cp;
1299 
1300         PetscCall(DMPlexGetCone(dm, p, &cone));
1301         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1302         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1303       }
1304     }
1305     PetscCall(PetscViewerFlush(viewer));
1306     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1307     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1308     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1309     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1310     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1311     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1312     PetscCall(PetscFree3(names, colors, lcolors));
1313     PetscCall(PetscBTDestroy(&wp));
1314   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1315     Vec                    cown, acown;
1316     VecScatter             sct;
1317     ISLocalToGlobalMapping g2l;
1318     IS                     gid, acis;
1319     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1320     MPI_Group              ggroup, ngroup;
1321     PetscScalar           *array, nid;
1322     const PetscInt        *idxs;
1323     PetscInt              *idxs2, *start, *adjacency, *work;
1324     PetscInt64             lm[3], gm[3];
1325     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1326     PetscMPIInt            d1, d2, rank;
1327 
1328     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1329     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1330 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1331     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1332 #endif
1333     if (ncomm != MPI_COMM_NULL) {
1334       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1335       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1336       d1 = 0;
1337       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1338       nid = d2;
1339       PetscCallMPI(MPI_Group_free(&ggroup));
1340       PetscCallMPI(MPI_Group_free(&ngroup));
1341       PetscCallMPI(MPI_Comm_free(&ncomm));
1342     } else nid = 0.0;
1343 
1344     /* Get connectivity */
1345     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1346     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1347 
1348     /* filter overlapped local cells */
1349     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1350     PetscCall(ISGetIndices(gid, &idxs));
1351     PetscCall(ISGetLocalSize(gid, &cum));
1352     PetscCall(PetscMalloc1(cum, &idxs2));
1353     for (c = cStart, cum = 0; c < cEnd; c++) {
1354       if (idxs[c - cStart] < 0) continue;
1355       idxs2[cum++] = idxs[c - cStart];
1356     }
1357     PetscCall(ISRestoreIndices(gid, &idxs));
1358     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1359     PetscCall(ISDestroy(&gid));
1360     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1361 
1362     /* support for node-aware cell locality */
1363     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1364     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1365     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1366     PetscCall(VecGetArray(cown, &array));
1367     for (c = 0; c < numVertices; c++) array[c] = nid;
1368     PetscCall(VecRestoreArray(cown, &array));
1369     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1370     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1371     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1372     PetscCall(ISDestroy(&acis));
1373     PetscCall(VecScatterDestroy(&sct));
1374     PetscCall(VecDestroy(&cown));
1375 
1376     /* compute edgeCut */
1377     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1378     PetscCall(PetscMalloc1(cum, &work));
1379     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1380     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1381     PetscCall(ISDestroy(&gid));
1382     PetscCall(VecGetArray(acown, &array));
1383     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1384       PetscInt totl;
1385 
1386       totl = start[c + 1] - start[c];
1387       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1388       for (i = 0; i < totl; i++) {
1389         if (work[i] < 0) {
1390           ect += 1;
1391           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1392         }
1393       }
1394     }
1395     PetscCall(PetscFree(work));
1396     PetscCall(VecRestoreArray(acown, &array));
1397     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1398     lm[1] = -numVertices;
1399     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1400     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1401     lm[0] = ect;                     /* edgeCut */
1402     lm[1] = ectn;                    /* node-aware edgeCut */
1403     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1404     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1405     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1406 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1407     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1408 #else
1409     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1410 #endif
1411     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1412     PetscCall(PetscFree(start));
1413     PetscCall(PetscFree(adjacency));
1414     PetscCall(VecDestroy(&acown));
1415   } else {
1416     const char    *name;
1417     PetscInt      *sizes, *hybsizes, *ghostsizes;
1418     PetscInt       locDepth, depth, cellHeight, dim, d;
1419     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1420     PetscInt       numLabels, l, maxSize = 17;
1421     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1422     MPI_Comm       comm;
1423     PetscMPIInt    size, rank;
1424 
1425     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1426     PetscCallMPI(MPI_Comm_size(comm, &size));
1427     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1428     PetscCall(DMGetDimension(dm, &dim));
1429     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1430     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1431     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1432     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1433     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1434     PetscCall(DMPlexGetDepth(dm, &locDepth));
1435     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1436     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1437     gcNum = gcEnd - gcStart;
1438     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1439     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1440     for (d = 0; d <= depth; d++) {
1441       PetscInt Nc[2] = {0, 0}, ict;
1442 
1443       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1444       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1445       ict = ct0;
1446       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1447       ct0 = (DMPolytopeType)ict;
1448       for (p = pStart; p < pEnd; ++p) {
1449         DMPolytopeType ct;
1450 
1451         PetscCall(DMPlexGetCellType(dm, p, &ct));
1452         if (ct == ct0) ++Nc[0];
1453         else ++Nc[1];
1454       }
1455       if (size < maxSize) {
1456         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1457         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1458         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1459         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1460         for (p = 0; p < size; ++p) {
1461           if (rank == 0) {
1462             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1463             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1464             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1465           }
1466         }
1467       } else {
1468         PetscInt locMinMax[2];
1469 
1470         locMinMax[0] = Nc[0] + Nc[1];
1471         locMinMax[1] = Nc[0] + Nc[1];
1472         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1473         locMinMax[0] = Nc[1];
1474         locMinMax[1] = Nc[1];
1475         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1476         if (d == depth) {
1477           locMinMax[0] = gcNum;
1478           locMinMax[1] = gcNum;
1479           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1480         }
1481         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1482         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1483         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1484         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1485       }
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1487     }
1488     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1489     {
1490       const PetscReal *maxCell;
1491       const PetscReal *L;
1492       PetscBool        localized;
1493 
1494       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1495       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1496       if (L || localized) {
1497         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1498         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1499         if (L) {
1500           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1501           for (d = 0; d < dim; ++d) {
1502             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1503             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1504           }
1505           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1506         }
1507         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1508         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1509       }
1510     }
1511     PetscCall(DMGetNumLabels(dm, &numLabels));
1512     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1513     for (l = 0; l < numLabels; ++l) {
1514       DMLabel         label;
1515       const char     *name;
1516       IS              valueIS;
1517       const PetscInt *values;
1518       PetscInt        numValues, v;
1519 
1520       PetscCall(DMGetLabelName(dm, l, &name));
1521       PetscCall(DMGetLabel(dm, name, &label));
1522       PetscCall(DMLabelGetNumValues(label, &numValues));
1523       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1524       PetscCall(DMLabelGetValueIS(label, &valueIS));
1525       PetscCall(ISGetIndices(valueIS, &values));
1526       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1527       for (v = 0; v < numValues; ++v) {
1528         PetscInt size;
1529 
1530         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1531         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1532         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1533       }
1534       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1535       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1536       PetscCall(ISRestoreIndices(valueIS, &values));
1537       PetscCall(ISDestroy(&valueIS));
1538     }
1539     {
1540       char    **labelNames;
1541       PetscInt  Nl = numLabels;
1542       PetscBool flg;
1543 
1544       PetscCall(PetscMalloc1(Nl, &labelNames));
1545       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1546       for (l = 0; l < Nl; ++l) {
1547         DMLabel label;
1548 
1549         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1550         if (flg) {
1551           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1552           PetscCall(DMLabelView(label, viewer));
1553         }
1554         PetscCall(PetscFree(labelNames[l]));
1555       }
1556       PetscCall(PetscFree(labelNames));
1557     }
1558     /* If no fields are specified, people do not want to see adjacency */
1559     if (dm->Nf) {
1560       PetscInt f;
1561 
1562       for (f = 0; f < dm->Nf; ++f) {
1563         const char *name;
1564 
1565         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1566         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1567         PetscCall(PetscViewerASCIIPushTab(viewer));
1568         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1569         if (dm->fields[f].adjacency[0]) {
1570           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1571           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1572         } else {
1573           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1574           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1575         }
1576         PetscCall(PetscViewerASCIIPopTab(viewer));
1577       }
1578     }
1579     PetscCall(DMGetCoarseDM(dm, &cdm));
1580     if (cdm) {
1581       PetscCall(PetscViewerASCIIPushTab(viewer));
1582       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1583       PetscCall(DMPlexView_Ascii(cdm, viewer));
1584       PetscCall(PetscViewerASCIIPopTab(viewer));
1585     }
1586   }
1587   PetscFunctionReturn(PETSC_SUCCESS);
1588 }
1589 
1590 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1591 {
1592   DMPolytopeType ct;
1593   PetscMPIInt    rank;
1594   PetscInt       cdim;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   PetscCall(DMGetCoordinateDim(dm, &cdim));
1600   switch (ct) {
1601   case DM_POLYTOPE_SEGMENT:
1602   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1603     switch (cdim) {
1604     case 1: {
1605       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1606       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1607 
1608       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1609       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1610       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1611     } break;
1612     case 2: {
1613       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1614       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1615       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1616 
1617       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1618       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1619       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1620     } break;
1621     default:
1622       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1623     }
1624     break;
1625   case DM_POLYTOPE_TRIANGLE:
1626     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1627     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1628     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1629     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1630     break;
1631   case DM_POLYTOPE_QUADRILATERAL:
1632     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1633     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1635     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1636     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1637     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1638     break;
1639   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1640     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1641     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1642     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1643     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1644     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1645     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1646     break;
1647   case DM_POLYTOPE_FV_GHOST:
1648     break;
1649   default:
1650     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1651   }
1652   PetscFunctionReturn(PETSC_SUCCESS);
1653 }
1654 
1655 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1656 {
1657   DMPolytopeType ct;
1658   PetscReal      centroid[2] = {0., 0.};
1659   PetscMPIInt    rank;
1660   PetscInt       fillColor, v, e, d;
1661 
1662   PetscFunctionBegin;
1663   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1664   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1665   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1666   switch (ct) {
1667   case DM_POLYTOPE_TRIANGLE: {
1668     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1669 
1670     for (v = 0; v < 3; ++v) {
1671       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1672       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1673     }
1674     for (e = 0; e < 3; ++e) {
1675       refCoords[0] = refVertices[e * 2 + 0];
1676       refCoords[1] = refVertices[e * 2 + 1];
1677       for (d = 1; d <= edgeDiv; ++d) {
1678         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1679         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1680       }
1681       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1682       for (d = 0; d < edgeDiv; ++d) {
1683         PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1684         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1685       }
1686     }
1687   } break;
1688   default:
1689     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1690   }
1691   PetscFunctionReturn(PETSC_SUCCESS);
1692 }
1693 
1694 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1695 {
1696   PetscDraw    draw;
1697   DM           cdm;
1698   PetscSection coordSection;
1699   Vec          coordinates;
1700   PetscReal    xyl[3], xyr[3];
1701   PetscReal   *refCoords, *edgeCoords;
1702   PetscBool    isnull, drawAffine = PETSC_TRUE;
1703   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1704 
1705   PetscFunctionBegin;
1706   PetscCall(DMGetCoordinateDim(dm, &dim));
1707   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1708   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1709   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1710   PetscCall(DMGetCoordinateDM(dm, &cdm));
1711   PetscCall(DMGetLocalSection(cdm, &coordSection));
1712   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1713   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1714   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1715 
1716   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1717   PetscCall(PetscDrawIsNull(draw, &isnull));
1718   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1719   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1720 
1721   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1722   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1723   PetscCall(PetscDrawClear(draw));
1724 
1725   for (c = cStart; c < cEnd; ++c) {
1726     PetscScalar       *coords = NULL;
1727     const PetscScalar *coords_arr;
1728     PetscInt           numCoords;
1729     PetscBool          isDG;
1730 
1731     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1732     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1733     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1734     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1735   }
1736   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1737   PetscCall(PetscDrawFlush(draw));
1738   PetscCall(PetscDrawPause(draw));
1739   PetscCall(PetscDrawSave(draw));
1740   PetscFunctionReturn(PETSC_SUCCESS);
1741 }
1742 
1743 #if defined(PETSC_HAVE_EXODUSII)
1744   #include <exodusII.h>
1745   #include <petscviewerexodusii.h>
1746 #endif
1747 
1748 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1749 {
1750   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1751   char      name[PETSC_MAX_PATH_LEN];
1752 
1753   PetscFunctionBegin;
1754   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1755   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1756   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1757   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1759   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1761   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1762   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1763   if (iascii) {
1764     PetscViewerFormat format;
1765     PetscCall(PetscViewerGetFormat(viewer, &format));
1766     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1767     else PetscCall(DMPlexView_Ascii(dm, viewer));
1768   } else if (ishdf5) {
1769 #if defined(PETSC_HAVE_HDF5)
1770     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1771 #else
1772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1773 #endif
1774   } else if (isvtk) {
1775     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1776   } else if (isdraw) {
1777     PetscCall(DMPlexView_Draw(dm, viewer));
1778   } else if (isglvis) {
1779     PetscCall(DMPlexView_GLVis(dm, viewer));
1780 #if defined(PETSC_HAVE_EXODUSII)
1781   } else if (isexodus) {
1782     /*
1783       exodusII requires that all sets be part of exactly one cell set.
1784       If the dm does not have a "Cell Sets" label defined, we create one
1785       with ID 1, containing all cells.
1786       Note that if the Cell Sets label is defined but does not cover all cells,
1787       we may still have a problem. This should probably be checked here or in the viewer;
1788     */
1789     PetscInt numCS;
1790     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1791     if (!numCS) {
1792       PetscInt cStart, cEnd, c;
1793       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1794       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1795       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1796     }
1797     PetscCall(DMView_PlexExodusII(dm, viewer));
1798 #endif
1799 #if defined(PETSC_HAVE_CGNS)
1800   } else if (iscgns) {
1801     PetscCall(DMView_PlexCGNS(dm, viewer));
1802 #endif
1803   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1804   /* Optionally view the partition */
1805   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1806   if (flg) {
1807     Vec ranks;
1808     PetscCall(DMPlexCreateRankField(dm, &ranks));
1809     PetscCall(VecView(ranks, viewer));
1810     PetscCall(VecDestroy(&ranks));
1811   }
1812   /* Optionally view a label */
1813   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1814   if (flg) {
1815     DMLabel label;
1816     Vec     val;
1817 
1818     PetscCall(DMGetLabel(dm, name, &label));
1819     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1820     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1821     PetscCall(VecView(val, viewer));
1822     PetscCall(VecDestroy(&val));
1823   }
1824   PetscFunctionReturn(PETSC_SUCCESS);
1825 }
1826 
1827 /*@
1828   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1829 
1830   Collective on dm
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: [](chapter_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 on dm
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: [](chapter_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 on dm
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: [](chapter_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 on dm
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: [](chapter_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 on dm
1990 
1991   Input Parameters:
1992 + dm        - The `DM` that represents the topology
1993 . viewer    - The `PetscViewer` to save data with
1994 . sectiondm - The `DM` that contains the global section on which vec is defined
1995 - vec       - The global vector to be saved
1996 
1997   Level: advanced
1998 
1999   Notes:
2000   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2001 
2002   Typical calling sequence:
2003 .vb
2004        DMCreate(PETSC_COMM_WORLD, &dm);
2005        DMSetType(dm, DMPLEX);
2006        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2007        DMClone(dm, &sectiondm);
2008        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2009        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2010        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2011        PetscSectionSetChart(section, pStart, pEnd);
2012        PetscSectionSetUp(section);
2013        DMSetLocalSection(sectiondm, section);
2014        PetscSectionDestroy(&section);
2015        DMGetGlobalVector(sectiondm, &vec);
2016        PetscObjectSetName((PetscObject)vec, "vec_name");
2017        DMPlexTopologyView(dm, viewer);
2018        DMPlexSectionView(dm, viewer, sectiondm);
2019        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2020        DMRestoreGlobalVector(sectiondm, &vec);
2021        DMDestroy(&sectiondm);
2022        DMDestroy(&dm);
2023 .ve
2024 
2025 .seealso: [](chapter_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 on dm
2066 
2067   Input Parameters:
2068 + dm        - The `DM` that represents the topology
2069 . viewer    - The `PetscViewer` to save data with
2070 . sectiondm - The `DM` that contains the local section on which vec is defined; may be the same as dm
2071 - vec       - The local vector to be saved
2072 
2073   Level: advanced
2074 
2075   Note:
2076   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2077 
2078   Typical calling sequence:
2079 .vb
2080        DMCreate(PETSC_COMM_WORLD, &dm);
2081        DMSetType(dm, DMPLEX);
2082        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2083        DMClone(dm, &sectiondm);
2084        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2085        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2086        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2087        PetscSectionSetChart(section, pStart, pEnd);
2088        PetscSectionSetUp(section);
2089        DMSetLocalSection(sectiondm, section);
2090        DMGetLocalVector(sectiondm, &vec);
2091        PetscObjectSetName((PetscObject)vec, "vec_name");
2092        DMPlexTopologyView(dm, viewer);
2093        DMPlexSectionView(dm, viewer, sectiondm);
2094        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2095        DMRestoreLocalVector(sectiondm, &vec);
2096        DMDestroy(&sectiondm);
2097        DMDestroy(&dm);
2098 .ve
2099 
2100 .seealso: [](chapter_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 on dm
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 Parameters:
2171 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded plex, where N is the global number of points; NULL if unneeded
2172 
2173   Level: advanced
2174 
2175 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2176           `PetscViewer`, `PetscSF`
2177 @*/
2178 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2179 {
2180   PetscBool ishdf5;
2181 
2182   PetscFunctionBegin;
2183   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2184   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2185   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2186   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2187   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2188   if (ishdf5) {
2189 #if defined(PETSC_HAVE_HDF5)
2190     PetscViewerFormat format;
2191     PetscCall(PetscViewerGetFormat(viewer, &format));
2192     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2193       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2194     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2195 #else
2196     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2197 #endif
2198   }
2199   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2200   PetscFunctionReturn(PETSC_SUCCESS);
2201 }
2202 
2203 /*@
2204   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2205 
2206   Collective on dm
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: [](chapter_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 on dm
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: [](chapter_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 on dm
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 SF that migrates any on-disk Vec data associated with sectionA into a global Vec associated with the sectiondm's global section (NULL if not needed)
2300 - localDofSF  - The SF that migrates any on-disk Vec data associated with sectionA into a local Vec associated with the sectiondm's local section (NULL if not needed)
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: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2335 @*/
2336 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2344   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2345   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2346   if (localDofSF) PetscValidPointer(localDofSF, 6);
2347   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2348   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2349   if (ishdf5) {
2350 #if defined(PETSC_HAVE_HDF5)
2351     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2352 #else
2353     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2354 #endif
2355   }
2356   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2357   PetscFunctionReturn(PETSC_SUCCESS);
2358 }
2359 
2360 /*@
2361   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2362 
2363   Collective on dm
2364 
2365   Input Parameters:
2366 + dm        - The `DM` that represents the topology
2367 . viewer    - The `PetscViewer` that represents the on-disk vector data
2368 . sectiondm - The `DM` that contains the global section on which vec is defined
2369 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2370 - vec       - The global vector to set values of
2371 
2372   Level: advanced
2373 
2374   Notes:
2375   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2376 
2377   Typical calling sequence:
2378 .vb
2379        DMCreate(PETSC_COMM_WORLD, &dm);
2380        DMSetType(dm, DMPLEX);
2381        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2382        DMPlexTopologyLoad(dm, viewer, &sfX);
2383        DMClone(dm, &sectiondm);
2384        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2385        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2386        DMGetGlobalVector(sectiondm, &vec);
2387        PetscObjectSetName((PetscObject)vec, "vec_name");
2388        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2389        DMRestoreGlobalVector(sectiondm, &vec);
2390        PetscSFDestroy(&gsf);
2391        PetscSFDestroy(&sfX);
2392        DMDestroy(&sectiondm);
2393        DMDestroy(&dm);
2394 .ve
2395 
2396 .seealso: [](chapter_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 on dm
2439 
2440   Input Parameters:
2441 + dm        - The `DM` that represents the topology
2442 . viewer    - The `PetscViewer` that represents the on-disk vector data
2443 . sectiondm - The `DM` that contains the local section on which vec is defined
2444 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2445 - vec       - The local vector to set values of
2446 
2447   Level: advanced
2448 
2449   Notes:
2450   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2451 
2452   Typical calling sequence:
2453 .vb
2454        DMCreate(PETSC_COMM_WORLD, &dm);
2455        DMSetType(dm, DMPLEX);
2456        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2457        DMPlexTopologyLoad(dm, viewer, &sfX);
2458        DMClone(dm, &sectiondm);
2459        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2460        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2461        DMGetLocalVector(sectiondm, &vec);
2462        PetscObjectSetName((PetscObject)vec, "vec_name");
2463        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2464        DMRestoreLocalVector(sectiondm, &vec);
2465        PetscSFDestroy(&lsf);
2466        PetscSFDestroy(&sfX);
2467        DMDestroy(&sectiondm);
2468        DMDestroy(&dm);
2469 .ve
2470 
2471 .seealso: [](chapter_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(DMPlexTransformDestroy(&mesh->tr));
2537   PetscCall(PetscFree(mesh->facesTmp));
2538   PetscCall(PetscFree(mesh->tetgenOpts));
2539   PetscCall(PetscFree(mesh->triangleOpts));
2540   PetscCall(PetscFree(mesh->transformType));
2541   PetscCall(PetscFree(mesh->distributionName));
2542   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2543   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2544   PetscCall(ISDestroy(&mesh->subpointIS));
2545   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2546   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2547   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2548   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2549   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2550   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2551   PetscCall(ISDestroy(&mesh->anchorIS));
2552   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2553   PetscCall(PetscFree(mesh->parents));
2554   PetscCall(PetscFree(mesh->childIDs));
2555   PetscCall(PetscSectionDestroy(&mesh->childSection));
2556   PetscCall(PetscFree(mesh->children));
2557   PetscCall(DMDestroy(&mesh->referenceTree));
2558   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2559   PetscCall(PetscFree(mesh->neighbors));
2560   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2561   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2562   PetscCall(PetscFree(mesh));
2563   PetscFunctionReturn(PETSC_SUCCESS);
2564 }
2565 
2566 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2567 {
2568   PetscSection           sectionGlobal;
2569   PetscInt               bs = -1, mbs;
2570   PetscInt               localSize, localStart = 0;
2571   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2572   MatType                mtype;
2573   ISLocalToGlobalMapping ltog;
2574 
2575   PetscFunctionBegin;
2576   PetscCall(MatInitializePackage());
2577   mtype = dm->mattype;
2578   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2579   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2580   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2581   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2582   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2583   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2584   PetscCall(MatSetType(*J, mtype));
2585   PetscCall(MatSetFromOptions(*J));
2586   PetscCall(MatGetBlockSize(*J, &mbs));
2587   if (mbs > 1) bs = mbs;
2588   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2589   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2590   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2591   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2592   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2593   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2594   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2595   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2596   if (!isShell) {
2597     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2598     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2599     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2600 
2601     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2602 
2603     PetscCall(PetscCalloc1(localSize, &pblocks));
2604     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2605     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2606     for (p = pStart; p < pEnd; ++p) {
2607       switch (dm->blocking_type) {
2608       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2609         PetscInt bdof, offset;
2610 
2611         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2612         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2613         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2614         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2615         dof  = dof < 0 ? -(dof + 1) : dof;
2616         bdof = cdof && (dof - cdof) ? 1 : dof;
2617         if (dof) {
2618           if (bs < 0) {
2619             bs = bdof;
2620           } else if (bs != bdof) {
2621             bs = 1;
2622           }
2623         }
2624       } break;
2625       case DM_BLOCKING_FIELD_NODE: {
2626         for (PetscInt field = 0; field < num_fields; field++) {
2627           PetscInt num_comp, bdof, offset;
2628           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2629           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2630           if (dof < 0) continue;
2631           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2632           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2633           PetscAssert(dof % num_comp == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " field %" PetscInt_FMT " has %" PetscInt_FMT " dof, not divisible by %" PetscInt_FMT " component ", p, field, dof, num_comp);
2634           PetscInt num_nodes = dof / num_comp;
2635           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2636           // Handle possibly constant block size (unlikely)
2637           bdof = cdof && (dof - cdof) ? 1 : dof;
2638           if (dof) {
2639             if (bs < 0) {
2640               bs = bdof;
2641             } else if (bs != bdof) {
2642               bs = 1;
2643             }
2644           }
2645         }
2646       } break;
2647       }
2648     }
2649     /* Must have same blocksize on all procs (some might have no points) */
2650     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2651     bsLocal[1] = bs;
2652     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2653     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2654     else bs = bsMinMax[0];
2655     bs = PetscMax(1, bs);
2656     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2657     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2658       PetscCall(MatSetBlockSize(*J, bs));
2659       PetscCall(MatSetUp(*J));
2660     } else {
2661       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2662       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2663       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2664     }
2665     { // Consolidate blocks
2666       PetscInt nblocks = 0;
2667       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2668         if (pblocks[i] == 0) continue;
2669         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2670         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2671       }
2672       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2673     }
2674     PetscCall(PetscFree(pblocks));
2675   }
2676   PetscCall(MatSetDM(*J, dm));
2677   PetscFunctionReturn(PETSC_SUCCESS);
2678 }
2679 
2680 /*@
2681   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2682 
2683   Not Collective
2684 
2685   Input Parameter:
2686 . mesh - The `DMPLEX`
2687 
2688   Output Parameters:
2689 . subsection - The subdomain section
2690 
2691   Level: developer
2692 
2693 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `PetscSection`
2694 @*/
2695 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2696 {
2697   DM_Plex *mesh = (DM_Plex *)dm->data;
2698 
2699   PetscFunctionBegin;
2700   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2701   if (!mesh->subdomainSection) {
2702     PetscSection section;
2703     PetscSF      sf;
2704 
2705     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2706     PetscCall(DMGetLocalSection(dm, &section));
2707     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2708     PetscCall(PetscSFDestroy(&sf));
2709   }
2710   *subsection = mesh->subdomainSection;
2711   PetscFunctionReturn(PETSC_SUCCESS);
2712 }
2713 
2714 /*@
2715   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2716 
2717   Not Collective
2718 
2719   Input Parameter:
2720 . mesh - The `DMPLEX`
2721 
2722   Output Parameters:
2723 + pStart - The first mesh point
2724 - pEnd   - The upper bound for mesh points
2725 
2726   Level: beginner
2727 
2728 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2729 @*/
2730 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2731 {
2732   DM_Plex *mesh = (DM_Plex *)dm->data;
2733 
2734   PetscFunctionBegin;
2735   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2736   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2737   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2738   PetscFunctionReturn(PETSC_SUCCESS);
2739 }
2740 
2741 /*@
2742   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2743 
2744   Not Collective
2745 
2746   Input Parameters:
2747 + mesh - The `DMPLEX`
2748 . pStart - The first mesh point
2749 - pEnd   - The upper bound for mesh points
2750 
2751   Level: beginner
2752 
2753 .seealso: [](chapter_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   PetscFunctionReturn(PETSC_SUCCESS);
2764 }
2765 
2766 /*@
2767   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2768 
2769   Not Collective
2770 
2771   Input Parameters:
2772 + mesh - The `DMPLEX`
2773 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2774 
2775   Output Parameter:
2776 . size - The cone size for point p
2777 
2778   Level: beginner
2779 
2780 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2781 @*/
2782 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2783 {
2784   DM_Plex *mesh = (DM_Plex *)dm->data;
2785 
2786   PetscFunctionBegin;
2787   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2788   PetscValidIntPointer(size, 3);
2789   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2790   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2791   PetscFunctionReturn(PETSC_SUCCESS);
2792 }
2793 
2794 /*@
2795   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2796 
2797   Not Collective
2798 
2799   Input Parameters:
2800 + mesh - The `DMPLEX`
2801 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2802 - size - The cone size for point p
2803 
2804   Level: beginner
2805 
2806   Note:
2807   This should be called after `DMPlexSetChart()`.
2808 
2809 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2810 @*/
2811 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2812 {
2813   DM_Plex *mesh = (DM_Plex *)dm->data;
2814 
2815   PetscFunctionBegin;
2816   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2817   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2818   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2819   PetscFunctionReturn(PETSC_SUCCESS);
2820 }
2821 
2822 /*@C
2823   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2824 
2825   Not Collective
2826 
2827   Input Parameters:
2828 + dm - The `DMPLEX`
2829 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2830 
2831   Output Parameter:
2832 . cone - An array of points which are on the in-edges for point p
2833 
2834   Level: beginner
2835 
2836   Fortran Note:
2837   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2838   `DMPlexRestoreCone()` is not needed/available in C.
2839 
2840 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2841 @*/
2842 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2843 {
2844   DM_Plex *mesh = (DM_Plex *)dm->data;
2845   PetscInt off;
2846 
2847   PetscFunctionBegin;
2848   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2849   PetscValidPointer(cone, 3);
2850   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2851   *cone = &mesh->cones[off];
2852   PetscFunctionReturn(PETSC_SUCCESS);
2853 }
2854 
2855 /*@C
2856   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2857 
2858   Not Collective
2859 
2860   Input Parameters:
2861 + dm - The `DMPLEX`
2862 - p - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2863 
2864   Output Parameters:
2865 + pConesSection - `PetscSection` describing the layout of pCones
2866 - pCones - An array of points which are on the in-edges for the point set p
2867 
2868   Level: intermediate
2869 
2870 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2871 @*/
2872 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2873 {
2874   PetscSection cs, newcs;
2875   PetscInt    *cones;
2876   PetscInt    *newarr = NULL;
2877   PetscInt     n;
2878 
2879   PetscFunctionBegin;
2880   PetscCall(DMPlexGetCones(dm, &cones));
2881   PetscCall(DMPlexGetConeSection(dm, &cs));
2882   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2883   if (pConesSection) *pConesSection = newcs;
2884   if (pCones) {
2885     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2886     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2887   }
2888   PetscFunctionReturn(PETSC_SUCCESS);
2889 }
2890 
2891 /*@
2892   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2893 
2894   Not Collective
2895 
2896   Input Parameters:
2897 + dm - The `DMPLEX`
2898 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2899 
2900   Output Parameter:
2901 . expandedPoints - An array of vertices recursively expanded from input points
2902 
2903   Level: advanced
2904 
2905   Notes:
2906   Like `DMPlexGetConeRecursive()` but returns only the 0-depth IS (i.e. vertices only) and no sections.
2907 
2908   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2909 
2910 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2911           `DMPlexGetDepth()`, `IS`
2912 @*/
2913 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2914 {
2915   IS      *expandedPointsAll;
2916   PetscInt depth;
2917 
2918   PetscFunctionBegin;
2919   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2920   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2921   PetscValidPointer(expandedPoints, 3);
2922   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2923   *expandedPoints = expandedPointsAll[0];
2924   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2925   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2926   PetscFunctionReturn(PETSC_SUCCESS);
2927 }
2928 
2929 /*@
2930   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).
2931 
2932   Not Collective
2933 
2934   Input Parameters:
2935 + dm - The `DMPLEX`
2936 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2937 
2938   Output Parameters:
2939 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2940 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2941 - sections - (optional) An array of sections which describe mappings from points to their cone points
2942 
2943   Level: advanced
2944 
2945   Notes:
2946   Like `DMPlexGetConeTuple()` but recursive.
2947 
2948   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.
2949   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2950 
2951   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:
2952   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2953   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2954 
2955 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2956           `DMPlexGetDepth()`, `PetscSection`, `IS`
2957 @*/
2958 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2959 {
2960   const PetscInt *arr0 = NULL, *cone = NULL;
2961   PetscInt       *arr = NULL, *newarr = NULL;
2962   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2963   IS             *expandedPoints_;
2964   PetscSection   *sections_;
2965 
2966   PetscFunctionBegin;
2967   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2968   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2969   if (depth) PetscValidIntPointer(depth, 3);
2970   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2971   if (sections) PetscValidPointer(sections, 5);
2972   PetscCall(ISGetLocalSize(points, &n));
2973   PetscCall(ISGetIndices(points, &arr0));
2974   PetscCall(DMPlexGetDepth(dm, &depth_));
2975   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2976   PetscCall(PetscCalloc1(depth_, &sections_));
2977   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2978   for (d = depth_ - 1; d >= 0; d--) {
2979     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2980     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2981     for (i = 0; i < n; i++) {
2982       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2983       if (arr[i] >= start && arr[i] < end) {
2984         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2985         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2986       } else {
2987         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2988       }
2989     }
2990     PetscCall(PetscSectionSetUp(sections_[d]));
2991     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2992     PetscCall(PetscMalloc1(newn, &newarr));
2993     for (i = 0; i < n; i++) {
2994       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2995       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2996       if (cn > 1) {
2997         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2998         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2999       } else {
3000         newarr[co] = arr[i];
3001       }
3002     }
3003     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3004     arr = newarr;
3005     n   = newn;
3006   }
3007   PetscCall(ISRestoreIndices(points, &arr0));
3008   *depth = depth_;
3009   if (expandedPoints) *expandedPoints = expandedPoints_;
3010   else {
3011     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3012     PetscCall(PetscFree(expandedPoints_));
3013   }
3014   if (sections) *sections = sections_;
3015   else {
3016     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3017     PetscCall(PetscFree(sections_));
3018   }
3019   PetscFunctionReturn(PETSC_SUCCESS);
3020 }
3021 
3022 /*@
3023   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3024 
3025   Not Collective
3026 
3027   Input Parameters:
3028 + dm - The `DMPLEX`
3029 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3030 
3031   Output Parameters:
3032 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3033 . expandedPoints - (optional) An array of recursively expanded cones
3034 - sections - (optional) An array of sections which describe mappings from points to their cone points
3035 
3036   Level: advanced
3037 
3038   Note:
3039   See `DMPlexGetConeRecursive()`
3040 
3041 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3042           `DMPlexGetDepth()`, `IS`, `PetscSection`
3043 @*/
3044 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3045 {
3046   PetscInt d, depth_;
3047 
3048   PetscFunctionBegin;
3049   PetscCall(DMPlexGetDepth(dm, &depth_));
3050   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3051   if (depth) *depth = 0;
3052   if (expandedPoints) {
3053     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3054     PetscCall(PetscFree(*expandedPoints));
3055   }
3056   if (sections) {
3057     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3058     PetscCall(PetscFree(*sections));
3059   }
3060   PetscFunctionReturn(PETSC_SUCCESS);
3061 }
3062 
3063 /*@
3064   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
3065 
3066   Not Collective
3067 
3068   Input Parameters:
3069 + mesh - The `DMPLEX`
3070 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3071 - cone - An array of points which are on the in-edges for point p
3072 
3073   Level: beginner
3074 
3075   Note:
3076   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3077 
3078 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3079 @*/
3080 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3081 {
3082   DM_Plex *mesh = (DM_Plex *)dm->data;
3083   PetscInt pStart, pEnd;
3084   PetscInt dof, off, c;
3085 
3086   PetscFunctionBegin;
3087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3088   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3089   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3090   if (dof) PetscValidIntPointer(cone, 3);
3091   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3092   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);
3093   for (c = 0; c < dof; ++c) {
3094     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);
3095     mesh->cones[off + c] = cone[c];
3096   }
3097   PetscFunctionReturn(PETSC_SUCCESS);
3098 }
3099 
3100 /*@C
3101   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3102 
3103   Not Collective
3104 
3105   Input Parameters:
3106 + mesh - The `DMPLEX`
3107 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3108 
3109   Output Parameter:
3110 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3111                     integer giving the prescription for cone traversal.
3112 
3113   Level: beginner
3114 
3115   Note:
3116   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3117   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3118   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3119   with the identity.
3120 
3121   Fortran Note:
3122   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3123   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3124 
3125 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3126 @*/
3127 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3128 {
3129   DM_Plex *mesh = (DM_Plex *)dm->data;
3130   PetscInt off;
3131 
3132   PetscFunctionBegin;
3133   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3134   if (PetscDefined(USE_DEBUG)) {
3135     PetscInt dof;
3136     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3137     if (dof) PetscValidPointer(coneOrientation, 3);
3138   }
3139   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3140 
3141   *coneOrientation = &mesh->coneOrientations[off];
3142   PetscFunctionReturn(PETSC_SUCCESS);
3143 }
3144 
3145 /*@
3146   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3147 
3148   Not Collective
3149 
3150   Input Parameters:
3151 + mesh - The `DMPLEX`
3152 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3153 - coneOrientation - An array of orientations
3154 
3155   Level: beginner
3156 
3157   Notes:
3158   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3159 
3160   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3161 
3162 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3163 @*/
3164 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3165 {
3166   DM_Plex *mesh = (DM_Plex *)dm->data;
3167   PetscInt pStart, pEnd;
3168   PetscInt dof, off, c;
3169 
3170   PetscFunctionBegin;
3171   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3172   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3173   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3174   if (dof) PetscValidIntPointer(coneOrientation, 3);
3175   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3176   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);
3177   for (c = 0; c < dof; ++c) {
3178     PetscInt cdof, o = coneOrientation[c];
3179 
3180     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3181     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);
3182     mesh->coneOrientations[off + c] = o;
3183   }
3184   PetscFunctionReturn(PETSC_SUCCESS);
3185 }
3186 
3187 /*@
3188   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3189 
3190   Not Collective
3191 
3192   Input Parameters:
3193 + mesh - The `DMPLEX`
3194 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3195 . conePos - The local index in the cone where the point should be put
3196 - conePoint - The mesh point to insert
3197 
3198   Level: beginner
3199 
3200 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3201 @*/
3202 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3203 {
3204   DM_Plex *mesh = (DM_Plex *)dm->data;
3205   PetscInt pStart, pEnd;
3206   PetscInt dof, off;
3207 
3208   PetscFunctionBegin;
3209   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3210   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3211   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);
3212   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);
3213   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3214   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3215   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);
3216   mesh->cones[off + conePos] = conePoint;
3217   PetscFunctionReturn(PETSC_SUCCESS);
3218 }
3219 
3220 /*@
3221   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3222 
3223   Not Collective
3224 
3225   Input Parameters:
3226 + mesh - The `DMPLEX`
3227 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3228 . conePos - The local index in the cone where the point should be put
3229 - coneOrientation - The point orientation to insert
3230 
3231   Level: beginner
3232 
3233   Note:
3234   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3235 
3236 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3237 @*/
3238 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3239 {
3240   DM_Plex *mesh = (DM_Plex *)dm->data;
3241   PetscInt pStart, pEnd;
3242   PetscInt dof, off;
3243 
3244   PetscFunctionBegin;
3245   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3246   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3247   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);
3248   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3249   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3250   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);
3251   mesh->coneOrientations[off + conePos] = coneOrientation;
3252   PetscFunctionReturn(PETSC_SUCCESS);
3253 }
3254 
3255 /*@C
3256   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3257 
3258   Not collective
3259 
3260   Input Parameters:
3261 + dm - The DMPlex
3262 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3263 
3264   Output Parameters:
3265 + cone - An array of points which are on the in-edges for point p
3266 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3267         integer giving the prescription for cone traversal.
3268 
3269   Level: beginner
3270 
3271   Notes:
3272   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3273   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3274   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3275   with the identity.
3276 
3277   Fortran Notes:
3278   Since it returns an array, this routine is only available in Fortran 90, and you must
3279   include petsc.h90 in your code.
3280   You must also call DMPlexRestoreCone() after you finish using the returned array.
3281   DMPlexRestoreCone() is not needed/available in C.
3282 
3283 .seealso: `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3284 @*/
3285 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3286 {
3287   DM_Plex *mesh = (DM_Plex *)dm->data;
3288 
3289   PetscFunctionBegin;
3290   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3291   if (mesh->tr) {
3292     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3293   } else {
3294     PetscInt off;
3295     if (PetscDefined(USE_DEBUG)) {
3296       PetscInt dof;
3297       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3298       if (dof) {
3299         if (cone) PetscValidPointer(cone, 3);
3300         if (ornt) PetscValidPointer(ornt, 4);
3301       }
3302     }
3303     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3304     if (cone) *cone = &mesh->cones[off];
3305     if (ornt) *ornt = &mesh->coneOrientations[off];
3306   }
3307   PetscFunctionReturn(PETSC_SUCCESS);
3308 }
3309 
3310 /*@C
3311   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3312 
3313   Not collective
3314 
3315   Input Parameters:
3316 + dm - The DMPlex
3317 . p  - The point, which must lie in the chart set with DMPlexSetChart()
3318 . cone - An array of points which are on the in-edges for point p
3319 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3320         integer giving the prescription for cone traversal.
3321 
3322   Level: beginner
3323 
3324   Notes:
3325   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3326   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3327   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3328   with the identity.
3329 
3330   Fortran Notes:
3331   Since it returns an array, this routine is only available in Fortran 90, and you must
3332   include petsc.h90 in your code.
3333   You must also call DMPlexRestoreCone() after you finish using the returned array.
3334   DMPlexRestoreCone() is not needed/available in C.
3335 
3336 .seealso: `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3337 @*/
3338 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3339 {
3340   DM_Plex *mesh = (DM_Plex *)dm->data;
3341 
3342   PetscFunctionBegin;
3343   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3344   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3345   PetscFunctionReturn(PETSC_SUCCESS);
3346 }
3347 
3348 /*@
3349   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3350 
3351   Not Collective
3352 
3353   Input Parameters:
3354 + mesh - The `DMPLEX`
3355 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3356 
3357   Output Parameter:
3358 . size - The support size for point p
3359 
3360   Level: beginner
3361 
3362 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3363 @*/
3364 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3365 {
3366   DM_Plex *mesh = (DM_Plex *)dm->data;
3367 
3368   PetscFunctionBegin;
3369   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3370   PetscValidIntPointer(size, 3);
3371   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3372   PetscFunctionReturn(PETSC_SUCCESS);
3373 }
3374 
3375 /*@
3376   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3377 
3378   Not Collective
3379 
3380   Input Parameters:
3381 + mesh - The `DMPLEX`
3382 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3383 - size - The support size for point p
3384 
3385   Level: beginner
3386 
3387   Note:
3388   This should be called after DMPlexSetChart().
3389 
3390 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3391 @*/
3392 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3393 {
3394   DM_Plex *mesh = (DM_Plex *)dm->data;
3395 
3396   PetscFunctionBegin;
3397   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3398   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3399   PetscFunctionReturn(PETSC_SUCCESS);
3400 }
3401 
3402 /*@C
3403   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3404 
3405   Not Collective
3406 
3407   Input Parameters:
3408 + mesh - The `DMPLEX`
3409 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3410 
3411   Output Parameter:
3412 . support - An array of points which are on the out-edges for point p
3413 
3414   Level: beginner
3415 
3416   Fortran Note:
3417   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3418   `DMPlexRestoreSupport()` is not needed/available in C.
3419 
3420 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3421 @*/
3422 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3423 {
3424   DM_Plex *mesh = (DM_Plex *)dm->data;
3425   PetscInt off;
3426 
3427   PetscFunctionBegin;
3428   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3429   PetscValidPointer(support, 3);
3430   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3431   *support = &mesh->supports[off];
3432   PetscFunctionReturn(PETSC_SUCCESS);
3433 }
3434 
3435 /*@
3436   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3437 
3438   Not Collective
3439 
3440   Input Parameters:
3441 + mesh - The `DMPLEX`
3442 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3443 - support - An array of points which are on the out-edges for point p
3444 
3445   Level: beginner
3446 
3447   Note:
3448   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3449 
3450 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3451 @*/
3452 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3453 {
3454   DM_Plex *mesh = (DM_Plex *)dm->data;
3455   PetscInt pStart, pEnd;
3456   PetscInt dof, off, c;
3457 
3458   PetscFunctionBegin;
3459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3460   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3461   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3462   if (dof) PetscValidIntPointer(support, 3);
3463   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3464   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);
3465   for (c = 0; c < dof; ++c) {
3466     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);
3467     mesh->supports[off + c] = support[c];
3468   }
3469   PetscFunctionReturn(PETSC_SUCCESS);
3470 }
3471 
3472 /*@
3473   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3474 
3475   Not Collective
3476 
3477   Input Parameters:
3478 + mesh - The `DMPLEX`
3479 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3480 . supportPos - The local index in the cone where the point should be put
3481 - supportPoint - The mesh point to insert
3482 
3483   Level: beginner
3484 
3485 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3486 @*/
3487 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3488 {
3489   DM_Plex *mesh = (DM_Plex *)dm->data;
3490   PetscInt pStart, pEnd;
3491   PetscInt dof, off;
3492 
3493   PetscFunctionBegin;
3494   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3495   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3496   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3497   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3498   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);
3499   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);
3500   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);
3501   mesh->supports[off + supportPos] = supportPoint;
3502   PetscFunctionReturn(PETSC_SUCCESS);
3503 }
3504 
3505 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3506 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3507 {
3508   switch (ct) {
3509   case DM_POLYTOPE_SEGMENT:
3510     if (o == -1) return -2;
3511     break;
3512   case DM_POLYTOPE_TRIANGLE:
3513     if (o == -3) return -1;
3514     if (o == -2) return -3;
3515     if (o == -1) return -2;
3516     break;
3517   case DM_POLYTOPE_QUADRILATERAL:
3518     if (o == -4) return -2;
3519     if (o == -3) return -1;
3520     if (o == -2) return -4;
3521     if (o == -1) return -3;
3522     break;
3523   default:
3524     return o;
3525   }
3526   return o;
3527 }
3528 
3529 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3530 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3531 {
3532   switch (ct) {
3533   case DM_POLYTOPE_SEGMENT:
3534     if ((o == -2) || (o == 1)) return -1;
3535     if (o == -1) return 0;
3536     break;
3537   case DM_POLYTOPE_TRIANGLE:
3538     if (o == -3) return -2;
3539     if (o == -2) return -1;
3540     if (o == -1) return -3;
3541     break;
3542   case DM_POLYTOPE_QUADRILATERAL:
3543     if (o == -4) return -2;
3544     if (o == -3) return -1;
3545     if (o == -2) return -4;
3546     if (o == -1) return -3;
3547     break;
3548   default:
3549     return o;
3550   }
3551   return o;
3552 }
3553 
3554 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3555 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3556 {
3557   PetscInt pStart, pEnd, p;
3558 
3559   PetscFunctionBegin;
3560   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3561   for (p = pStart; p < pEnd; ++p) {
3562     const PetscInt *cone, *ornt;
3563     PetscInt        coneSize, c;
3564 
3565     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3566     PetscCall(DMPlexGetCone(dm, p, &cone));
3567     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3568     for (c = 0; c < coneSize; ++c) {
3569       DMPolytopeType ct;
3570       const PetscInt o = ornt[c];
3571 
3572       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3573       switch (ct) {
3574       case DM_POLYTOPE_SEGMENT:
3575         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3576         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3577         break;
3578       case DM_POLYTOPE_TRIANGLE:
3579         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3580         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3581         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3582         break;
3583       case DM_POLYTOPE_QUADRILATERAL:
3584         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3585         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3586         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3587         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3588         break;
3589       default:
3590         break;
3591       }
3592     }
3593   }
3594   PetscFunctionReturn(PETSC_SUCCESS);
3595 }
3596 
3597 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3598 {
3599   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3600   PetscInt       *closure;
3601   const PetscInt *tmp = NULL, *tmpO = NULL;
3602   PetscInt        off = 0, tmpSize, t;
3603 
3604   PetscFunctionBeginHot;
3605   if (ornt) {
3606     PetscCall(DMPlexGetCellType(dm, p, &ct));
3607     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3608   }
3609   if (*points) {
3610     closure = *points;
3611   } else {
3612     PetscInt maxConeSize, maxSupportSize;
3613     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3614     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3615   }
3616   if (useCone) {
3617     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3618     PetscCall(DMPlexGetCone(dm, p, &tmp));
3619     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3620   } else {
3621     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3622     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3623   }
3624   if (ct == DM_POLYTOPE_UNKNOWN) {
3625     closure[off++] = p;
3626     closure[off++] = 0;
3627     for (t = 0; t < tmpSize; ++t) {
3628       closure[off++] = tmp[t];
3629       closure[off++] = tmpO ? tmpO[t] : 0;
3630     }
3631   } else {
3632     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3633 
3634     /* We assume that cells with a valid type have faces with a valid type */
3635     closure[off++] = p;
3636     closure[off++] = ornt;
3637     for (t = 0; t < tmpSize; ++t) {
3638       DMPolytopeType ft;
3639 
3640       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3641       closure[off++] = tmp[arr[t]];
3642       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3643     }
3644   }
3645   if (numPoints) *numPoints = tmpSize + 1;
3646   if (points) *points = closure;
3647   PetscFunctionReturn(PETSC_SUCCESS);
3648 }
3649 
3650 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3651 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3652 {
3653   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3654   const PetscInt *cone, *ornt;
3655   PetscInt       *pts, *closure = NULL;
3656   DMPolytopeType  ft;
3657   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3658   PetscInt        dim, coneSize, c, d, clSize, cl;
3659 
3660   PetscFunctionBeginHot;
3661   PetscCall(DMGetDimension(dm, &dim));
3662   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3663   PetscCall(DMPlexGetCone(dm, point, &cone));
3664   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3665   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3666   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3667   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3668   maxSize       = PetscMax(coneSeries, supportSeries);
3669   if (*points) {
3670     pts = *points;
3671   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3672   c        = 0;
3673   pts[c++] = point;
3674   pts[c++] = o;
3675   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3676   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3677   for (cl = 0; cl < clSize * 2; cl += 2) {
3678     pts[c++] = closure[cl];
3679     pts[c++] = closure[cl + 1];
3680   }
3681   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3682   for (cl = 0; cl < clSize * 2; cl += 2) {
3683     pts[c++] = closure[cl];
3684     pts[c++] = closure[cl + 1];
3685   }
3686   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3687   for (d = 2; d < coneSize; ++d) {
3688     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3689     pts[c++] = cone[arr[d * 2 + 0]];
3690     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3691   }
3692   if (dim >= 3) {
3693     for (d = 2; d < coneSize; ++d) {
3694       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3695       const PetscInt *fcone, *fornt;
3696       PetscInt        fconeSize, fc, i;
3697 
3698       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3699       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3700       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3701       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3702       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3703       for (fc = 0; fc < fconeSize; ++fc) {
3704         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3705         const PetscInt co = farr[fc * 2 + 1];
3706 
3707         for (i = 0; i < c; i += 2)
3708           if (pts[i] == cp) break;
3709         if (i == c) {
3710           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3711           pts[c++] = cp;
3712           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3713         }
3714       }
3715     }
3716   }
3717   *numPoints = c / 2;
3718   *points    = pts;
3719   PetscFunctionReturn(PETSC_SUCCESS);
3720 }
3721 
3722 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3723 {
3724   DMPolytopeType ct;
3725   PetscInt      *closure, *fifo;
3726   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3727   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3728   PetscInt       depth, maxSize;
3729 
3730   PetscFunctionBeginHot;
3731   PetscCall(DMPlexGetDepth(dm, &depth));
3732   if (depth == 1) {
3733     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3734     PetscFunctionReturn(PETSC_SUCCESS);
3735   }
3736   PetscCall(DMPlexGetCellType(dm, p, &ct));
3737   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3738   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3739     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3740     PetscFunctionReturn(PETSC_SUCCESS);
3741   }
3742   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3743   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3744   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3745   maxSize       = PetscMax(coneSeries, supportSeries);
3746   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3747   if (*points) {
3748     closure = *points;
3749   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3750   closure[closureSize++] = p;
3751   closure[closureSize++] = ornt;
3752   fifo[fifoSize++]       = p;
3753   fifo[fifoSize++]       = ornt;
3754   fifo[fifoSize++]       = ct;
3755   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3756   while (fifoSize - fifoStart) {
3757     const PetscInt       q    = fifo[fifoStart++];
3758     const PetscInt       o    = fifo[fifoStart++];
3759     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3760     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3761     const PetscInt      *tmp, *tmpO;
3762     PetscInt             tmpSize, t;
3763 
3764     if (PetscDefined(USE_DEBUG)) {
3765       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3766       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);
3767     }
3768     if (useCone) {
3769       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3770       PetscCall(DMPlexGetCone(dm, q, &tmp));
3771       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3772     } else {
3773       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3774       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3775       tmpO = NULL;
3776     }
3777     for (t = 0; t < tmpSize; ++t) {
3778       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3779       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3780       const PetscInt cp = tmp[ip];
3781       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3782       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3783       PetscInt       c;
3784 
3785       /* Check for duplicate */
3786       for (c = 0; c < closureSize; c += 2) {
3787         if (closure[c] == cp) break;
3788       }
3789       if (c == closureSize) {
3790         closure[closureSize++] = cp;
3791         closure[closureSize++] = co;
3792         fifo[fifoSize++]       = cp;
3793         fifo[fifoSize++]       = co;
3794         fifo[fifoSize++]       = ct;
3795       }
3796     }
3797   }
3798   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3799   if (numPoints) *numPoints = closureSize / 2;
3800   if (points) *points = closure;
3801   PetscFunctionReturn(PETSC_SUCCESS);
3802 }
3803 
3804 /*@C
3805   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3806 
3807   Not Collective
3808 
3809   Input Parameters:
3810 + dm      - The `DMPLEX`
3811 . p       - The mesh point
3812 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3813 
3814   Input/Output Parameter:
3815 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3816            if NULL on input, internal storage will be returned, otherwise the provided array is used
3817 
3818   Output Parameter:
3819 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3820 
3821   Level: beginner
3822 
3823   Note:
3824   If using internal storage (points is NULL on input), each call overwrites the last output.
3825 
3826   Fortran Note:
3827   The numPoints argument is not present in the Fortran binding since it is internal to the array.
3828 
3829 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3830 @*/
3831 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3832 {
3833   PetscFunctionBeginHot;
3834   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3835   if (numPoints) PetscValidIntPointer(numPoints, 4);
3836   if (points) PetscValidPointer(points, 5);
3837   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3838   PetscFunctionReturn(PETSC_SUCCESS);
3839 }
3840 
3841 /*@C
3842   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3843 
3844   Not Collective
3845 
3846   Input Parameters:
3847 + dm        - The `DMPLEX`
3848 . p         - The mesh point
3849 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3850 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3851 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3852 
3853   Level: beginner
3854 
3855   Note:
3856   If not using internal storage (points is not NULL on input), this call is unnecessary
3857 
3858 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3859 @*/
3860 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3861 {
3862   PetscFunctionBeginHot;
3863   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3864   if (numPoints) *numPoints = 0;
3865   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3866   PetscFunctionReturn(PETSC_SUCCESS);
3867 }
3868 
3869 /*@
3870   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3871 
3872   Not Collective
3873 
3874   Input Parameter:
3875 . mesh - The `DMPLEX`
3876 
3877   Output Parameters:
3878 + maxConeSize - The maximum number of in-edges
3879 - maxSupportSize - The maximum number of out-edges
3880 
3881   Level: beginner
3882 
3883 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3884 @*/
3885 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3886 {
3887   DM_Plex *mesh = (DM_Plex *)dm->data;
3888 
3889   PetscFunctionBegin;
3890   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3891   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3892   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3893   PetscFunctionReturn(PETSC_SUCCESS);
3894 }
3895 
3896 PetscErrorCode DMSetUp_Plex(DM dm)
3897 {
3898   DM_Plex *mesh = (DM_Plex *)dm->data;
3899   PetscInt size, maxSupportSize;
3900 
3901   PetscFunctionBegin;
3902   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3903   PetscCall(PetscSectionSetUp(mesh->coneSection));
3904   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3905   PetscCall(PetscMalloc1(size, &mesh->cones));
3906   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3907   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3908   if (maxSupportSize) {
3909     PetscCall(PetscSectionSetUp(mesh->supportSection));
3910     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3911     PetscCall(PetscMalloc1(size, &mesh->supports));
3912   }
3913   PetscFunctionReturn(PETSC_SUCCESS);
3914 }
3915 
3916 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3917 {
3918   PetscFunctionBegin;
3919   if (subdm) PetscCall(DMClone(dm, subdm));
3920   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3921   if (subdm) (*subdm)->useNatural = dm->useNatural;
3922   if (dm->useNatural && dm->sfMigration) {
3923     PetscSF sfNatural;
3924 
3925     (*subdm)->sfMigration = dm->sfMigration;
3926     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3927     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3928     (*subdm)->sfNatural = sfNatural;
3929   }
3930   PetscFunctionReturn(PETSC_SUCCESS);
3931 }
3932 
3933 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3934 {
3935   PetscInt i = 0;
3936 
3937   PetscFunctionBegin;
3938   PetscCall(DMClone(dms[0], superdm));
3939   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3940   (*superdm)->useNatural = PETSC_FALSE;
3941   for (i = 0; i < len; i++) {
3942     if (dms[i]->useNatural && dms[i]->sfMigration) {
3943       PetscSF sfNatural;
3944 
3945       (*superdm)->sfMigration = dms[i]->sfMigration;
3946       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3947       (*superdm)->useNatural = PETSC_TRUE;
3948       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3949       (*superdm)->sfNatural = sfNatural;
3950       break;
3951     }
3952   }
3953   PetscFunctionReturn(PETSC_SUCCESS);
3954 }
3955 
3956 /*@
3957   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3958 
3959   Not Collective
3960 
3961   Input Parameter:
3962 . mesh - The `DMPLEX`
3963 
3964   Level: beginner
3965 
3966   Note:
3967   This should be called after all calls to `DMPlexSetCone()`
3968 
3969 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3970 @*/
3971 PetscErrorCode DMPlexSymmetrize(DM dm)
3972 {
3973   DM_Plex  *mesh = (DM_Plex *)dm->data;
3974   PetscInt *offsets;
3975   PetscInt  supportSize;
3976   PetscInt  pStart, pEnd, p;
3977 
3978   PetscFunctionBegin;
3979   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3980   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3981   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3982   /* Calculate support sizes */
3983   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3984   for (p = pStart; p < pEnd; ++p) {
3985     PetscInt dof, off, c;
3986 
3987     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3988     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3989     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3990   }
3991   PetscCall(PetscSectionSetUp(mesh->supportSection));
3992   /* Calculate supports */
3993   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3994   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3995   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3996   for (p = pStart; p < pEnd; ++p) {
3997     PetscInt dof, off, c;
3998 
3999     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4000     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4001     for (c = off; c < off + dof; ++c) {
4002       const PetscInt q = mesh->cones[c];
4003       PetscInt       offS;
4004 
4005       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4006 
4007       mesh->supports[offS + offsets[q]] = p;
4008       ++offsets[q];
4009     }
4010   }
4011   PetscCall(PetscFree(offsets));
4012   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4013   PetscFunctionReturn(PETSC_SUCCESS);
4014 }
4015 
4016 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4017 {
4018   IS stratumIS;
4019 
4020   PetscFunctionBegin;
4021   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4022   if (PetscDefined(USE_DEBUG)) {
4023     PetscInt  qStart, qEnd, numLevels, level;
4024     PetscBool overlap = PETSC_FALSE;
4025     PetscCall(DMLabelGetNumValues(label, &numLevels));
4026     for (level = 0; level < numLevels; level++) {
4027       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4028       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4029         overlap = PETSC_TRUE;
4030         break;
4031       }
4032     }
4033     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);
4034   }
4035   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4036   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4037   PetscCall(ISDestroy(&stratumIS));
4038   PetscFunctionReturn(PETSC_SUCCESS);
4039 }
4040 
4041 /*@
4042   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4043   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4044   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4045   the DAG.
4046 
4047   Collective on dm
4048 
4049   Input Parameter:
4050 . mesh - The `DMPLEX`
4051 
4052   Level: beginner
4053 
4054   Notes:
4055   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4056   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4057   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4058   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4059   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4060 
4061   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4062   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4063   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
4064   to interpolate only that one (e0), so that
4065 .vb
4066   cone(c0) = {e0, v2}
4067   cone(e0) = {v0, v1}
4068 .ve
4069   If `DMPlexStratify()` is run on this mesh, it will give depths
4070 .vb
4071    depth 0 = {v0, v1, v2}
4072    depth 1 = {e0, c0}
4073 .ve
4074   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4075 
4076   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4077 
4078 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4079 @*/
4080 PetscErrorCode DMPlexStratify(DM dm)
4081 {
4082   DM_Plex *mesh = (DM_Plex *)dm->data;
4083   DMLabel  label;
4084   PetscInt pStart, pEnd, p;
4085   PetscInt numRoots = 0, numLeaves = 0;
4086 
4087   PetscFunctionBegin;
4088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4089   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4090 
4091   /* Create depth label */
4092   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4093   PetscCall(DMCreateLabel(dm, "depth"));
4094   PetscCall(DMPlexGetDepthLabel(dm, &label));
4095 
4096   {
4097     /* Initialize roots and count leaves */
4098     PetscInt sMin = PETSC_MAX_INT;
4099     PetscInt sMax = PETSC_MIN_INT;
4100     PetscInt coneSize, supportSize;
4101 
4102     for (p = pStart; p < pEnd; ++p) {
4103       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4104       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4105       if (!coneSize && supportSize) {
4106         sMin = PetscMin(p, sMin);
4107         sMax = PetscMax(p, sMax);
4108         ++numRoots;
4109       } else if (!supportSize && coneSize) {
4110         ++numLeaves;
4111       } else if (!supportSize && !coneSize) {
4112         /* Isolated points */
4113         sMin = PetscMin(p, sMin);
4114         sMax = PetscMax(p, sMax);
4115       }
4116     }
4117     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4118   }
4119 
4120   if (numRoots + numLeaves == (pEnd - pStart)) {
4121     PetscInt sMin = PETSC_MAX_INT;
4122     PetscInt sMax = PETSC_MIN_INT;
4123     PetscInt coneSize, supportSize;
4124 
4125     for (p = pStart; p < pEnd; ++p) {
4126       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4127       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4128       if (!supportSize && coneSize) {
4129         sMin = PetscMin(p, sMin);
4130         sMax = PetscMax(p, sMax);
4131       }
4132     }
4133     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4134   } else {
4135     PetscInt level = 0;
4136     PetscInt qStart, qEnd, q;
4137 
4138     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4139     while (qEnd > qStart) {
4140       PetscInt sMin = PETSC_MAX_INT;
4141       PetscInt sMax = PETSC_MIN_INT;
4142 
4143       for (q = qStart; q < qEnd; ++q) {
4144         const PetscInt *support;
4145         PetscInt        supportSize, s;
4146 
4147         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4148         PetscCall(DMPlexGetSupport(dm, q, &support));
4149         for (s = 0; s < supportSize; ++s) {
4150           sMin = PetscMin(support[s], sMin);
4151           sMax = PetscMax(support[s], sMax);
4152         }
4153       }
4154       PetscCall(DMLabelGetNumValues(label, &level));
4155       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4156       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4157     }
4158   }
4159   { /* just in case there is an empty process */
4160     PetscInt numValues, maxValues = 0, v;
4161 
4162     PetscCall(DMLabelGetNumValues(label, &numValues));
4163     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4164     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4165   }
4166   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4167   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4168   PetscFunctionReturn(PETSC_SUCCESS);
4169 }
4170 
4171 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4172 {
4173   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4174   PetscInt       dim, depth, pheight, coneSize;
4175 
4176   PetscFunctionBeginHot;
4177   PetscCall(DMGetDimension(dm, &dim));
4178   PetscCall(DMPlexGetDepth(dm, &depth));
4179   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4180   pheight = depth - pdepth;
4181   if (depth <= 1) {
4182     switch (pdepth) {
4183     case 0:
4184       ct = DM_POLYTOPE_POINT;
4185       break;
4186     case 1:
4187       switch (coneSize) {
4188       case 2:
4189         ct = DM_POLYTOPE_SEGMENT;
4190         break;
4191       case 3:
4192         ct = DM_POLYTOPE_TRIANGLE;
4193         break;
4194       case 4:
4195         switch (dim) {
4196         case 2:
4197           ct = DM_POLYTOPE_QUADRILATERAL;
4198           break;
4199         case 3:
4200           ct = DM_POLYTOPE_TETRAHEDRON;
4201           break;
4202         default:
4203           break;
4204         }
4205         break;
4206       case 5:
4207         ct = DM_POLYTOPE_PYRAMID;
4208         break;
4209       case 6:
4210         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4211         break;
4212       case 8:
4213         ct = DM_POLYTOPE_HEXAHEDRON;
4214         break;
4215       default:
4216         break;
4217       }
4218     }
4219   } else {
4220     if (pdepth == 0) {
4221       ct = DM_POLYTOPE_POINT;
4222     } else if (pheight == 0) {
4223       switch (dim) {
4224       case 1:
4225         switch (coneSize) {
4226         case 2:
4227           ct = DM_POLYTOPE_SEGMENT;
4228           break;
4229         default:
4230           break;
4231         }
4232         break;
4233       case 2:
4234         switch (coneSize) {
4235         case 3:
4236           ct = DM_POLYTOPE_TRIANGLE;
4237           break;
4238         case 4:
4239           ct = DM_POLYTOPE_QUADRILATERAL;
4240           break;
4241         default:
4242           break;
4243         }
4244         break;
4245       case 3:
4246         switch (coneSize) {
4247         case 4:
4248           ct = DM_POLYTOPE_TETRAHEDRON;
4249           break;
4250         case 5: {
4251           const PetscInt *cone;
4252           PetscInt        faceConeSize;
4253 
4254           PetscCall(DMPlexGetCone(dm, p, &cone));
4255           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4256           switch (faceConeSize) {
4257           case 3:
4258             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4259             break;
4260           case 4:
4261             ct = DM_POLYTOPE_PYRAMID;
4262             break;
4263           }
4264         } break;
4265         case 6:
4266           ct = DM_POLYTOPE_HEXAHEDRON;
4267           break;
4268         default:
4269           break;
4270         }
4271         break;
4272       default:
4273         break;
4274       }
4275     } else if (pheight > 0) {
4276       switch (coneSize) {
4277       case 2:
4278         ct = DM_POLYTOPE_SEGMENT;
4279         break;
4280       case 3:
4281         ct = DM_POLYTOPE_TRIANGLE;
4282         break;
4283       case 4:
4284         ct = DM_POLYTOPE_QUADRILATERAL;
4285         break;
4286       default:
4287         break;
4288       }
4289     }
4290   }
4291   *pt = ct;
4292   PetscFunctionReturn(PETSC_SUCCESS);
4293 }
4294 
4295 /*@
4296   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4297 
4298   Collective on dm
4299 
4300   Input Parameter:
4301 . mesh - The `DMPLEX`
4302 
4303   Level: developer
4304 
4305   Note:
4306   This function is normally called automatically when a cell type is requested. It creates an
4307   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4308   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4309 
4310   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4311 
4312 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4313 @*/
4314 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4315 {
4316   DM_Plex *mesh;
4317   DMLabel  ctLabel;
4318   PetscInt pStart, pEnd, p;
4319 
4320   PetscFunctionBegin;
4321   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4322   mesh = (DM_Plex *)dm->data;
4323   PetscCall(DMCreateLabel(dm, "celltype"));
4324   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4325   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4326   for (p = pStart; p < pEnd; ++p) {
4327     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4328     PetscInt       pdepth;
4329 
4330     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4331     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4332     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4333     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4334   }
4335   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4336   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4337   PetscFunctionReturn(PETSC_SUCCESS);
4338 }
4339 
4340 /*@C
4341   DMPlexGetJoin - Get an array for the join of the set of points
4342 
4343   Not Collective
4344 
4345   Input Parameters:
4346 + dm - The `DMPLEX` object
4347 . numPoints - The number of input points for the join
4348 - points - The input points
4349 
4350   Output Parameters:
4351 + numCoveredPoints - The number of points in the join
4352 - coveredPoints - The points in the join
4353 
4354   Level: intermediate
4355 
4356   Note:
4357   Currently, this is restricted to a single level join
4358 
4359   Fortran Note:
4360   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4361 
4362 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4363 @*/
4364 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4365 {
4366   DM_Plex  *mesh = (DM_Plex *)dm->data;
4367   PetscInt *join[2];
4368   PetscInt  joinSize, i = 0;
4369   PetscInt  dof, off, p, c, m;
4370   PetscInt  maxSupportSize;
4371 
4372   PetscFunctionBegin;
4373   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4374   PetscValidIntPointer(points, 3);
4375   PetscValidIntPointer(numCoveredPoints, 4);
4376   PetscValidPointer(coveredPoints, 5);
4377   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4378   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4379   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4380   /* Copy in support of first point */
4381   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4382   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4383   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4384   /* Check each successive support */
4385   for (p = 1; p < numPoints; ++p) {
4386     PetscInt newJoinSize = 0;
4387 
4388     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4389     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4390     for (c = 0; c < dof; ++c) {
4391       const PetscInt point = mesh->supports[off + c];
4392 
4393       for (m = 0; m < joinSize; ++m) {
4394         if (point == join[i][m]) {
4395           join[1 - i][newJoinSize++] = point;
4396           break;
4397         }
4398       }
4399     }
4400     joinSize = newJoinSize;
4401     i        = 1 - i;
4402   }
4403   *numCoveredPoints = joinSize;
4404   *coveredPoints    = join[i];
4405   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4406   PetscFunctionReturn(PETSC_SUCCESS);
4407 }
4408 
4409 /*@C
4410   DMPlexRestoreJoin - Restore an array for the join of the set of points
4411 
4412   Not Collective
4413 
4414   Input Parameters:
4415 + dm - The `DMPLEX` object
4416 . numPoints - The number of input points for the join
4417 - points - The input points
4418 
4419   Output Parameters:
4420 + numCoveredPoints - The number of points in the join
4421 - coveredPoints - The points in the join
4422 
4423   Level: intermediate
4424 
4425   Fortran Note:
4426   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4427 
4428 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4429 @*/
4430 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4431 {
4432   PetscFunctionBegin;
4433   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4434   if (points) PetscValidIntPointer(points, 3);
4435   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4436   PetscValidPointer(coveredPoints, 5);
4437   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4438   if (numCoveredPoints) *numCoveredPoints = 0;
4439   PetscFunctionReturn(PETSC_SUCCESS);
4440 }
4441 
4442 /*@C
4443   DMPlexGetFullJoin - Get an array for the join of the set of points
4444 
4445   Not Collective
4446 
4447   Input Parameters:
4448 + dm - The `DMPLEX` object
4449 . numPoints - The number of input points for the join
4450 - points - The input points
4451 
4452   Output Parameters:
4453 + numCoveredPoints - The number of points in the join
4454 - coveredPoints - The points in the join
4455 
4456   Level: intermediate
4457 
4458   Fortran Note:
4459   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4460 
4461 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4462 @*/
4463 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4464 {
4465   PetscInt *offsets, **closures;
4466   PetscInt *join[2];
4467   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4468   PetscInt  p, d, c, m, ms;
4469 
4470   PetscFunctionBegin;
4471   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4472   PetscValidIntPointer(points, 3);
4473   PetscValidIntPointer(numCoveredPoints, 4);
4474   PetscValidPointer(coveredPoints, 5);
4475 
4476   PetscCall(DMPlexGetDepth(dm, &depth));
4477   PetscCall(PetscCalloc1(numPoints, &closures));
4478   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4479   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4480   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4481   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4482   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4483 
4484   for (p = 0; p < numPoints; ++p) {
4485     PetscInt closureSize;
4486 
4487     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4488 
4489     offsets[p * (depth + 2) + 0] = 0;
4490     for (d = 0; d < depth + 1; ++d) {
4491       PetscInt pStart, pEnd, i;
4492 
4493       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4494       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4495         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4496           offsets[p * (depth + 2) + d + 1] = i;
4497           break;
4498         }
4499       }
4500       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4501     }
4502     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);
4503   }
4504   for (d = 0; d < depth + 1; ++d) {
4505     PetscInt dof;
4506 
4507     /* Copy in support of first point */
4508     dof = offsets[d + 1] - offsets[d];
4509     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4510     /* Check each successive cone */
4511     for (p = 1; p < numPoints && joinSize; ++p) {
4512       PetscInt newJoinSize = 0;
4513 
4514       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4515       for (c = 0; c < dof; ++c) {
4516         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4517 
4518         for (m = 0; m < joinSize; ++m) {
4519           if (point == join[i][m]) {
4520             join[1 - i][newJoinSize++] = point;
4521             break;
4522           }
4523         }
4524       }
4525       joinSize = newJoinSize;
4526       i        = 1 - i;
4527     }
4528     if (joinSize) break;
4529   }
4530   *numCoveredPoints = joinSize;
4531   *coveredPoints    = join[i];
4532   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4533   PetscCall(PetscFree(closures));
4534   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4535   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4536   PetscFunctionReturn(PETSC_SUCCESS);
4537 }
4538 
4539 /*@C
4540   DMPlexGetMeet - Get an array for the meet of the set of points
4541 
4542   Not Collective
4543 
4544   Input Parameters:
4545 + dm - The `DMPLEX` object
4546 . numPoints - The number of input points for the meet
4547 - points - The input points
4548 
4549   Output Parameters:
4550 + numCoveredPoints - The number of points in the meet
4551 - coveredPoints - The points in the meet
4552 
4553   Level: intermediate
4554 
4555   Note:
4556   Currently, this is restricted to a single level meet
4557 
4558   Fortran Notes:
4559   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4560 
4561 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4562 @*/
4563 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4564 {
4565   DM_Plex  *mesh = (DM_Plex *)dm->data;
4566   PetscInt *meet[2];
4567   PetscInt  meetSize, i = 0;
4568   PetscInt  dof, off, p, c, m;
4569   PetscInt  maxConeSize;
4570 
4571   PetscFunctionBegin;
4572   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4573   PetscValidIntPointer(points, 3);
4574   PetscValidIntPointer(numCoveringPoints, 4);
4575   PetscValidPointer(coveringPoints, 5);
4576   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4577   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4578   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4579   /* Copy in cone of first point */
4580   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4581   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4582   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4583   /* Check each successive cone */
4584   for (p = 1; p < numPoints; ++p) {
4585     PetscInt newMeetSize = 0;
4586 
4587     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4588     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4589     for (c = 0; c < dof; ++c) {
4590       const PetscInt point = mesh->cones[off + c];
4591 
4592       for (m = 0; m < meetSize; ++m) {
4593         if (point == meet[i][m]) {
4594           meet[1 - i][newMeetSize++] = point;
4595           break;
4596         }
4597       }
4598     }
4599     meetSize = newMeetSize;
4600     i        = 1 - i;
4601   }
4602   *numCoveringPoints = meetSize;
4603   *coveringPoints    = meet[i];
4604   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4605   PetscFunctionReturn(PETSC_SUCCESS);
4606 }
4607 
4608 /*@C
4609   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4610 
4611   Not Collective
4612 
4613   Input Parameters:
4614 + dm - The `DMPLEX` object
4615 . numPoints - The number of input points for the meet
4616 - points - The input points
4617 
4618   Output Parameters:
4619 + numCoveredPoints - The number of points in the meet
4620 - coveredPoints - The points in the meet
4621 
4622   Level: intermediate
4623 
4624   Fortran Note:
4625   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4626 
4627 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4628 @*/
4629 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4630 {
4631   PetscFunctionBegin;
4632   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4633   if (points) PetscValidIntPointer(points, 3);
4634   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4635   PetscValidPointer(coveredPoints, 5);
4636   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4637   if (numCoveredPoints) *numCoveredPoints = 0;
4638   PetscFunctionReturn(PETSC_SUCCESS);
4639 }
4640 
4641 /*@C
4642   DMPlexGetFullMeet - Get an array for the meet of the set of points
4643 
4644   Not Collective
4645 
4646   Input Parameters:
4647 + dm - The `DMPLEX` object
4648 . numPoints - The number of input points for the meet
4649 - points - The input points
4650 
4651   Output Parameters:
4652 + numCoveredPoints - The number of points in the meet
4653 - coveredPoints - The points in the meet
4654 
4655   Level: intermediate
4656 
4657   Fortran Note:
4658   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4659 
4660 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4661 @*/
4662 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4663 {
4664   PetscInt *offsets, **closures;
4665   PetscInt *meet[2];
4666   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4667   PetscInt  p, h, c, m, mc;
4668 
4669   PetscFunctionBegin;
4670   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4671   PetscValidIntPointer(points, 3);
4672   PetscValidIntPointer(numCoveredPoints, 4);
4673   PetscValidPointer(coveredPoints, 5);
4674 
4675   PetscCall(DMPlexGetDepth(dm, &height));
4676   PetscCall(PetscMalloc1(numPoints, &closures));
4677   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4678   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4679   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4680   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4681   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4682 
4683   for (p = 0; p < numPoints; ++p) {
4684     PetscInt closureSize;
4685 
4686     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4687 
4688     offsets[p * (height + 2) + 0] = 0;
4689     for (h = 0; h < height + 1; ++h) {
4690       PetscInt pStart, pEnd, i;
4691 
4692       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4693       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4694         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4695           offsets[p * (height + 2) + h + 1] = i;
4696           break;
4697         }
4698       }
4699       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4700     }
4701     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);
4702   }
4703   for (h = 0; h < height + 1; ++h) {
4704     PetscInt dof;
4705 
4706     /* Copy in cone of first point */
4707     dof = offsets[h + 1] - offsets[h];
4708     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4709     /* Check each successive cone */
4710     for (p = 1; p < numPoints && meetSize; ++p) {
4711       PetscInt newMeetSize = 0;
4712 
4713       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4714       for (c = 0; c < dof; ++c) {
4715         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4716 
4717         for (m = 0; m < meetSize; ++m) {
4718           if (point == meet[i][m]) {
4719             meet[1 - i][newMeetSize++] = point;
4720             break;
4721           }
4722         }
4723       }
4724       meetSize = newMeetSize;
4725       i        = 1 - i;
4726     }
4727     if (meetSize) break;
4728   }
4729   *numCoveredPoints = meetSize;
4730   *coveredPoints    = meet[i];
4731   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4732   PetscCall(PetscFree(closures));
4733   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4734   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4735   PetscFunctionReturn(PETSC_SUCCESS);
4736 }
4737 
4738 /*@C
4739   DMPlexEqual - Determine if two `DM` have the same topology
4740 
4741   Not Collective
4742 
4743   Input Parameters:
4744 + dmA - A `DMPLEX` object
4745 - dmB - A `DMPLEX` object
4746 
4747   Output Parameters:
4748 . equal - `PETSC_TRUE` if the topologies are identical
4749 
4750   Level: intermediate
4751 
4752   Note:
4753   We are not solving graph isomorphism, so we do not permute.
4754 
4755 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4756 @*/
4757 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4758 {
4759   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4760 
4761   PetscFunctionBegin;
4762   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4763   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4764   PetscValidBoolPointer(equal, 3);
4765 
4766   *equal = PETSC_FALSE;
4767   PetscCall(DMPlexGetDepth(dmA, &depth));
4768   PetscCall(DMPlexGetDepth(dmB, &depthB));
4769   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4770   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4771   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4772   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4773   for (p = pStart; p < pEnd; ++p) {
4774     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4775     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4776 
4777     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4778     PetscCall(DMPlexGetCone(dmA, p, &cone));
4779     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4780     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4781     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4782     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4783     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4784     for (c = 0; c < coneSize; ++c) {
4785       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4786       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4787     }
4788     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4789     PetscCall(DMPlexGetSupport(dmA, p, &support));
4790     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4791     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4792     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4793     for (s = 0; s < supportSize; ++s) {
4794       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4795     }
4796   }
4797   *equal = PETSC_TRUE;
4798   PetscFunctionReturn(PETSC_SUCCESS);
4799 }
4800 
4801 /*@C
4802   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4803 
4804   Not Collective
4805 
4806   Input Parameters:
4807 + dm         - The `DMPLEX`
4808 . cellDim    - The cell dimension
4809 - numCorners - The number of vertices on a cell
4810 
4811   Output Parameters:
4812 . numFaceVertices - The number of vertices on a face
4813 
4814   Level: developer
4815 
4816   Note:
4817   Of course this can only work for a restricted set of symmetric shapes
4818 
4819 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4820 @*/
4821 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4822 {
4823   MPI_Comm comm;
4824 
4825   PetscFunctionBegin;
4826   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4827   PetscValidIntPointer(numFaceVertices, 4);
4828   switch (cellDim) {
4829   case 0:
4830     *numFaceVertices = 0;
4831     break;
4832   case 1:
4833     *numFaceVertices = 1;
4834     break;
4835   case 2:
4836     switch (numCorners) {
4837     case 3:                 /* triangle */
4838       *numFaceVertices = 2; /* Edge has 2 vertices */
4839       break;
4840     case 4:                 /* quadrilateral */
4841       *numFaceVertices = 2; /* Edge has 2 vertices */
4842       break;
4843     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4844       *numFaceVertices = 3; /* Edge has 3 vertices */
4845       break;
4846     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4847       *numFaceVertices = 3; /* Edge has 3 vertices */
4848       break;
4849     default:
4850       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4851     }
4852     break;
4853   case 3:
4854     switch (numCorners) {
4855     case 4:                 /* tetradehdron */
4856       *numFaceVertices = 3; /* Face has 3 vertices */
4857       break;
4858     case 6:                 /* tet cohesive cells */
4859       *numFaceVertices = 4; /* Face has 4 vertices */
4860       break;
4861     case 8:                 /* hexahedron */
4862       *numFaceVertices = 4; /* Face has 4 vertices */
4863       break;
4864     case 9:                 /* tet cohesive Lagrange cells */
4865       *numFaceVertices = 6; /* Face has 6 vertices */
4866       break;
4867     case 10:                /* quadratic tetrahedron */
4868       *numFaceVertices = 6; /* Face has 6 vertices */
4869       break;
4870     case 12:                /* hex cohesive Lagrange cells */
4871       *numFaceVertices = 6; /* Face has 6 vertices */
4872       break;
4873     case 18:                /* quadratic tet cohesive Lagrange cells */
4874       *numFaceVertices = 6; /* Face has 6 vertices */
4875       break;
4876     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4877       *numFaceVertices = 9; /* Face has 9 vertices */
4878       break;
4879     default:
4880       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4881     }
4882     break;
4883   default:
4884     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4885   }
4886   PetscFunctionReturn(PETSC_SUCCESS);
4887 }
4888 
4889 /*@
4890   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4891 
4892   Not Collective
4893 
4894   Input Parameter:
4895 . dm    - The `DMPLEX` object
4896 
4897   Output Parameter:
4898 . depthLabel - The `DMLabel` recording point depth
4899 
4900   Level: developer
4901 
4902 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4903 @*/
4904 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4905 {
4906   PetscFunctionBegin;
4907   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4908   PetscValidPointer(depthLabel, 2);
4909   *depthLabel = dm->depthLabel;
4910   PetscFunctionReturn(PETSC_SUCCESS);
4911 }
4912 
4913 /*@
4914   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4915 
4916   Not Collective
4917 
4918   Input Parameter:
4919 . dm    - The `DMPLEX` object
4920 
4921   Output Parameter:
4922 . depth - The number of strata (breadth first levels) in the DAG
4923 
4924   Level: developer
4925 
4926   Notes:
4927   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4928 
4929   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4930 
4931   An empty mesh gives -1.
4932 
4933 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4934 @*/
4935 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4936 {
4937   DM_Plex *mesh = (DM_Plex *)dm->data;
4938   DMLabel  label;
4939   PetscInt d = 0;
4940 
4941   PetscFunctionBegin;
4942   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4943   PetscValidIntPointer(depth, 2);
4944   if (mesh->tr) {
4945     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4946   } else {
4947     PetscCall(DMPlexGetDepthLabel(dm, &label));
4948     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4949     *depth = d - 1;
4950   }
4951   PetscFunctionReturn(PETSC_SUCCESS);
4952 }
4953 
4954 /*@
4955   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4956 
4957   Not Collective
4958 
4959   Input Parameters:
4960 + dm    - The `DMPLEX` object
4961 - depth - The requested depth
4962 
4963   Output Parameters:
4964 + start - The first point at this depth
4965 - end   - One beyond the last point at this depth
4966 
4967   Level: developer
4968 
4969   Notes:
4970   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4971   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
4972   higher dimension, e.g., "edges".
4973 
4974 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4975 @*/
4976 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4977 {
4978   DM_Plex *mesh = (DM_Plex *)dm->data;
4979   DMLabel  label;
4980   PetscInt pStart, pEnd;
4981 
4982   PetscFunctionBegin;
4983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4984   if (start) {
4985     PetscValidIntPointer(start, 3);
4986     *start = 0;
4987   }
4988   if (end) {
4989     PetscValidIntPointer(end, 4);
4990     *end = 0;
4991   }
4992   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4993   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4994   if (depth < 0) {
4995     if (start) *start = pStart;
4996     if (end) *end = pEnd;
4997     PetscFunctionReturn(PETSC_SUCCESS);
4998   }
4999   if (mesh->tr) {
5000     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5001   } else {
5002     PetscCall(DMPlexGetDepthLabel(dm, &label));
5003     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5004     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5005   }
5006   PetscFunctionReturn(PETSC_SUCCESS);
5007 }
5008 
5009 /*@
5010   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
5011 
5012   Not Collective
5013 
5014   Input Parameters:
5015 + dm     - The `DMPLEX` object
5016 - height - The requested height
5017 
5018   Output Parameters:
5019 + start - The first point at this height
5020 - end   - One beyond the last point at this height
5021 
5022   Level: developer
5023 
5024   Notes:
5025   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5026   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5027   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5028 
5029 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5030 @*/
5031 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5032 {
5033   DMLabel  label;
5034   PetscInt depth, pStart, pEnd;
5035 
5036   PetscFunctionBegin;
5037   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5038   if (start) {
5039     PetscValidIntPointer(start, 3);
5040     *start = 0;
5041   }
5042   if (end) {
5043     PetscValidIntPointer(end, 4);
5044     *end = 0;
5045   }
5046   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5047   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5048   if (height < 0) {
5049     if (start) *start = pStart;
5050     if (end) *end = pEnd;
5051     PetscFunctionReturn(PETSC_SUCCESS);
5052   }
5053   PetscCall(DMPlexGetDepthLabel(dm, &label));
5054   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5055   PetscCall(DMLabelGetNumValues(label, &depth));
5056   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5057   PetscFunctionReturn(PETSC_SUCCESS);
5058 }
5059 
5060 /*@
5061   DMPlexGetPointDepth - Get the depth of a given point
5062 
5063   Not Collective
5064 
5065   Input Parameters:
5066 + dm    - The `DMPLEX` object
5067 - point - The point
5068 
5069   Output Parameter:
5070 . depth - The depth of the point
5071 
5072   Level: intermediate
5073 
5074 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5075 @*/
5076 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5077 {
5078   PetscFunctionBegin;
5079   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5080   PetscValidIntPointer(depth, 3);
5081   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5082   PetscFunctionReturn(PETSC_SUCCESS);
5083 }
5084 
5085 /*@
5086   DMPlexGetPointHeight - Get the height of a given point
5087 
5088   Not Collective
5089 
5090   Input Parameters:
5091 + dm    - The `DMPLEX` object
5092 - point - The point
5093 
5094   Output Parameter:
5095 . height - The height of the point
5096 
5097   Level: intermediate
5098 
5099 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5100 @*/
5101 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5102 {
5103   PetscInt n, pDepth;
5104 
5105   PetscFunctionBegin;
5106   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5107   PetscValidIntPointer(height, 3);
5108   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5109   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5110   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5111   PetscFunctionReturn(PETSC_SUCCESS);
5112 }
5113 
5114 /*@
5115   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5116 
5117   Not Collective
5118 
5119   Input Parameter:
5120 . dm - The `DMPLEX` object
5121 
5122   Output Parameter:
5123 . celltypeLabel - The `DMLabel` recording cell polytope type
5124 
5125   Level: developer
5126 
5127   Note:
5128   This function will trigger automatica computation of cell types. This can be disabled by calling
5129   `DMCreateLabel`(dm, "celltype") beforehand.
5130 
5131 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5132 @*/
5133 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5134 {
5135   PetscFunctionBegin;
5136   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5137   PetscValidPointer(celltypeLabel, 2);
5138   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5139   *celltypeLabel = dm->celltypeLabel;
5140   PetscFunctionReturn(PETSC_SUCCESS);
5141 }
5142 
5143 /*@
5144   DMPlexGetCellType - Get the polytope type of a given cell
5145 
5146   Not Collective
5147 
5148   Input Parameters:
5149 + dm   - The `DMPLEX` object
5150 - cell - The cell
5151 
5152   Output Parameter:
5153 . celltype - The polytope type of the cell
5154 
5155   Level: intermediate
5156 
5157 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5158 @*/
5159 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5160 {
5161   DM_Plex *mesh = (DM_Plex *)dm->data;
5162   DMLabel  label;
5163   PetscInt ct;
5164 
5165   PetscFunctionBegin;
5166   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5167   PetscValidPointer(celltype, 3);
5168   if (mesh->tr) {
5169     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5170   } else {
5171     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5172     PetscCall(DMLabelGetValue(label, cell, &ct));
5173     PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5174     *celltype = (DMPolytopeType)ct;
5175   }
5176   PetscFunctionReturn(PETSC_SUCCESS);
5177 }
5178 
5179 /*@
5180   DMPlexSetCellType - Set the polytope type of a given cell
5181 
5182   Not Collective
5183 
5184   Input Parameters:
5185 + dm   - The `DMPLEX` object
5186 . cell - The cell
5187 - celltype - The polytope type of the cell
5188 
5189   Level: advanced
5190 
5191   Note:
5192   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5193   is executed. This function will override the computed type. However, if automatic classification will not succeed
5194   and a user wants to manually specify all types, the classification must be disabled by calling
5195   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5196 
5197 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5198 @*/
5199 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5200 {
5201   DMLabel label;
5202 
5203   PetscFunctionBegin;
5204   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5205   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5206   PetscCall(DMLabelSetValue(label, cell, celltype));
5207   PetscFunctionReturn(PETSC_SUCCESS);
5208 }
5209 
5210 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5211 {
5212   PetscSection section, s;
5213   Mat          m;
5214   PetscInt     maxHeight;
5215   const char  *prefix;
5216 
5217   PetscFunctionBegin;
5218   PetscCall(DMClone(dm, cdm));
5219   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5220   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5221   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5222   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5223   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5224   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5225   PetscCall(DMSetLocalSection(*cdm, section));
5226   PetscCall(PetscSectionDestroy(&section));
5227   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5228   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5229   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5230   PetscCall(PetscSectionDestroy(&s));
5231   PetscCall(MatDestroy(&m));
5232 
5233   PetscCall(DMSetNumFields(*cdm, 1));
5234   PetscCall(DMCreateDS(*cdm));
5235   (*cdm)->cloneOpts = PETSC_TRUE;
5236   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5237   PetscFunctionReturn(PETSC_SUCCESS);
5238 }
5239 
5240 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5241 {
5242   Vec coordsLocal, cellCoordsLocal;
5243   DM  coordsDM, cellCoordsDM;
5244 
5245   PetscFunctionBegin;
5246   *field = NULL;
5247   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5248   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5249   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5250   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5251   if (coordsLocal && coordsDM) {
5252     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5253     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5254   }
5255   PetscFunctionReturn(PETSC_SUCCESS);
5256 }
5257 
5258 /*@C
5259   DMPlexGetConeSection - Return a section which describes the layout of cone data
5260 
5261   Not Collective
5262 
5263   Input Parameters:
5264 . dm        - The `DMPLEX` object
5265 
5266   Output Parameter:
5267 . section - The `PetscSection` object
5268 
5269   Level: developer
5270 
5271 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5272 @*/
5273 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5274 {
5275   DM_Plex *mesh = (DM_Plex *)dm->data;
5276 
5277   PetscFunctionBegin;
5278   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5279   if (section) *section = mesh->coneSection;
5280   PetscFunctionReturn(PETSC_SUCCESS);
5281 }
5282 
5283 /*@C
5284   DMPlexGetSupportSection - Return a section which describes the layout of support data
5285 
5286   Not Collective
5287 
5288   Input Parameters:
5289 . dm        - The `DMPLEX` object
5290 
5291   Output Parameter:
5292 . section - The `PetscSection` object
5293 
5294   Level: developer
5295 
5296 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5297 @*/
5298 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5299 {
5300   DM_Plex *mesh = (DM_Plex *)dm->data;
5301 
5302   PetscFunctionBegin;
5303   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5304   if (section) *section = mesh->supportSection;
5305   PetscFunctionReturn(PETSC_SUCCESS);
5306 }
5307 
5308 /*@C
5309   DMPlexGetCones - Return cone data
5310 
5311   Not Collective
5312 
5313   Input Parameters:
5314 . dm        - The `DMPLEX` object
5315 
5316   Output Parameter:
5317 . cones - The cone for each point
5318 
5319   Level: developer
5320 
5321 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5322 @*/
5323 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5324 {
5325   DM_Plex *mesh = (DM_Plex *)dm->data;
5326 
5327   PetscFunctionBegin;
5328   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5329   if (cones) *cones = mesh->cones;
5330   PetscFunctionReturn(PETSC_SUCCESS);
5331 }
5332 
5333 /*@C
5334   DMPlexGetConeOrientations - Return cone orientation data
5335 
5336   Not Collective
5337 
5338   Input Parameters:
5339 . dm        - The `DMPLEX` object
5340 
5341   Output Parameter:
5342 . coneOrientations - The array of cone orientations for all points
5343 
5344   Level: developer
5345 
5346   Notes:
5347   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5348 
5349   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5350 
5351 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5352 @*/
5353 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5354 {
5355   DM_Plex *mesh = (DM_Plex *)dm->data;
5356 
5357   PetscFunctionBegin;
5358   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5359   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5360   PetscFunctionReturn(PETSC_SUCCESS);
5361 }
5362 
5363 /******************************** FEM Support **********************************/
5364 
5365 /*
5366  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5367  representing a line in the section.
5368 */
5369 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5370 {
5371   PetscFunctionBeginHot;
5372   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5373   if (line < 0) {
5374     *k  = 0;
5375     *Nc = 0;
5376   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5377     *k = 1;
5378   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5379     /* An order k SEM disc has k-1 dofs on an edge */
5380     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5381     *k = *k / *Nc + 1;
5382   }
5383   PetscFunctionReturn(PETSC_SUCCESS);
5384 }
5385 
5386 /*@
5387 
5388   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5389   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5390   section provided (or the section of the DM).
5391 
5392   Input Parameters:
5393 + dm      - The DM
5394 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5395 - section - The PetscSection to reorder, or NULL for the default section
5396 
5397   Example:
5398   A typical interpolated single-quad mesh might order points as
5399 .vb
5400   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5401 
5402   v4 -- e6 -- v3
5403   |           |
5404   e7    c0    e8
5405   |           |
5406   v1 -- e5 -- v2
5407 .ve
5408 
5409   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5410   dofs in the order of points, e.g.,
5411 .vb
5412     c0 -> [0,1,2,3]
5413     v1 -> [4]
5414     ...
5415     e5 -> [8, 9]
5416 .ve
5417 
5418   which corresponds to the dofs
5419 .vb
5420     6   10  11  7
5421     13  2   3   15
5422     12  0   1   14
5423     4   8   9   5
5424 .ve
5425 
5426   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5427 .vb
5428   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5429 .ve
5430 
5431   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5432 .vb
5433    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5434 .ve
5435 
5436   Level: developer
5437 
5438   Note:
5439   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5440   degree of the basis.
5441 
5442 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5443 @*/
5444 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5445 {
5446   DMLabel   label;
5447   PetscInt  dim, depth = -1, eStart = -1, Nf;
5448   PetscBool vertexchart;
5449 
5450   PetscFunctionBegin;
5451   PetscCall(DMGetDimension(dm, &dim));
5452   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5453   if (point < 0) {
5454     PetscInt sStart, sEnd;
5455 
5456     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5457     point = sEnd - sStart ? sStart : point;
5458   }
5459   PetscCall(DMPlexGetDepthLabel(dm, &label));
5460   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5461   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5462   if (depth == 1) {
5463     eStart = point;
5464   } else if (depth == dim) {
5465     const PetscInt *cone;
5466 
5467     PetscCall(DMPlexGetCone(dm, point, &cone));
5468     if (dim == 2) eStart = cone[0];
5469     else if (dim == 3) {
5470       const PetscInt *cone2;
5471       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5472       eStart = cone2[0];
5473     } 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);
5474   } 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);
5475   { /* Determine whether the chart covers all points or just vertices. */
5476     PetscInt pStart, pEnd, cStart, cEnd;
5477     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5478     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5479     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5480     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5481     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5482   }
5483   PetscCall(PetscSectionGetNumFields(section, &Nf));
5484   for (PetscInt d = 1; d <= dim; d++) {
5485     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5486     PetscInt *perm;
5487 
5488     for (f = 0; f < Nf; ++f) {
5489       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5490       size += PetscPowInt(k + 1, d) * Nc;
5491     }
5492     PetscCall(PetscMalloc1(size, &perm));
5493     for (f = 0; f < Nf; ++f) {
5494       switch (d) {
5495       case 1:
5496         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5497         /*
5498          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5499          We want              [ vtx0; edge of length k-1; vtx1 ]
5500          */
5501         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5502         for (i = 0; i < k - 1; i++)
5503           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5504         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5505         foffset = offset;
5506         break;
5507       case 2:
5508         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5509         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5510         /* The SEM order is
5511 
5512          v_lb, {e_b}, v_rb,
5513          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5514          v_lt, reverse {e_t}, v_rt
5515          */
5516         {
5517           const PetscInt of   = 0;
5518           const PetscInt oeb  = of + PetscSqr(k - 1);
5519           const PetscInt oer  = oeb + (k - 1);
5520           const PetscInt oet  = oer + (k - 1);
5521           const PetscInt oel  = oet + (k - 1);
5522           const PetscInt ovlb = oel + (k - 1);
5523           const PetscInt ovrb = ovlb + 1;
5524           const PetscInt ovrt = ovrb + 1;
5525           const PetscInt ovlt = ovrt + 1;
5526           PetscInt       o;
5527 
5528           /* bottom */
5529           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5530           for (o = oeb; o < oer; ++o)
5531             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5532           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5533           /* middle */
5534           for (i = 0; i < k - 1; ++i) {
5535             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5536             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5537               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5538             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5539           }
5540           /* top */
5541           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5542           for (o = oel - 1; o >= oet; --o)
5543             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5544           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5545           foffset = offset;
5546         }
5547         break;
5548       case 3:
5549         /* The original hex closure is
5550 
5551          {c,
5552          f_b, f_t, f_f, f_b, f_r, f_l,
5553          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5554          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5555          */
5556         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5557         /* The SEM order is
5558          Bottom Slice
5559          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5560          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5561          v_blb, {e_bb}, v_brb,
5562 
5563          Middle Slice (j)
5564          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5565          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5566          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5567 
5568          Top Slice
5569          v_tlf, {e_tf}, v_trf,
5570          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5571          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5572          */
5573         {
5574           const PetscInt oc    = 0;
5575           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5576           const PetscInt oft   = ofb + PetscSqr(k - 1);
5577           const PetscInt off   = oft + PetscSqr(k - 1);
5578           const PetscInt ofk   = off + PetscSqr(k - 1);
5579           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5580           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5581           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5582           const PetscInt oebb  = oebl + (k - 1);
5583           const PetscInt oebr  = oebb + (k - 1);
5584           const PetscInt oebf  = oebr + (k - 1);
5585           const PetscInt oetf  = oebf + (k - 1);
5586           const PetscInt oetr  = oetf + (k - 1);
5587           const PetscInt oetb  = oetr + (k - 1);
5588           const PetscInt oetl  = oetb + (k - 1);
5589           const PetscInt oerf  = oetl + (k - 1);
5590           const PetscInt oelf  = oerf + (k - 1);
5591           const PetscInt oelb  = oelf + (k - 1);
5592           const PetscInt oerb  = oelb + (k - 1);
5593           const PetscInt ovblf = oerb + (k - 1);
5594           const PetscInt ovblb = ovblf + 1;
5595           const PetscInt ovbrb = ovblb + 1;
5596           const PetscInt ovbrf = ovbrb + 1;
5597           const PetscInt ovtlf = ovbrf + 1;
5598           const PetscInt ovtrf = ovtlf + 1;
5599           const PetscInt ovtrb = ovtrf + 1;
5600           const PetscInt ovtlb = ovtrb + 1;
5601           PetscInt       o, n;
5602 
5603           /* Bottom Slice */
5604           /*   bottom */
5605           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5606           for (o = oetf - 1; o >= oebf; --o)
5607             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5608           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5609           /*   middle */
5610           for (i = 0; i < k - 1; ++i) {
5611             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5612             for (n = 0; n < k - 1; ++n) {
5613               o = ofb + n * (k - 1) + i;
5614               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5615             }
5616             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5617           }
5618           /*   top */
5619           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5620           for (o = oebb; o < oebr; ++o)
5621             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5622           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5623 
5624           /* Middle Slice */
5625           for (j = 0; j < k - 1; ++j) {
5626             /*   bottom */
5627             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5628             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5629               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5630             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5631             /*   middle */
5632             for (i = 0; i < k - 1; ++i) {
5633               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5634               for (n = 0; n < k - 1; ++n)
5635                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5636               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5637             }
5638             /*   top */
5639             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5640             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5641               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5642             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5643           }
5644 
5645           /* Top Slice */
5646           /*   bottom */
5647           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5648           for (o = oetf; o < oetr; ++o)
5649             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5650           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5651           /*   middle */
5652           for (i = 0; i < k - 1; ++i) {
5653             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5654             for (n = 0; n < k - 1; ++n)
5655               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5656             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5657           }
5658           /*   top */
5659           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5660           for (o = oetl - 1; o >= oetb; --o)
5661             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5662           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5663 
5664           foffset = offset;
5665         }
5666         break;
5667       default:
5668         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5669       }
5670     }
5671     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5672     /* Check permutation */
5673     {
5674       PetscInt *check;
5675 
5676       PetscCall(PetscMalloc1(size, &check));
5677       for (i = 0; i < size; ++i) {
5678         check[i] = -1;
5679         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5680       }
5681       for (i = 0; i < size; ++i) check[perm[i]] = i;
5682       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5683       PetscCall(PetscFree(check));
5684     }
5685     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5686     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5687       PetscInt *loc_perm;
5688       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5689       for (PetscInt i = 0; i < size; i++) {
5690         loc_perm[i]        = perm[i];
5691         loc_perm[size + i] = size + perm[i];
5692       }
5693       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5694     }
5695   }
5696   PetscFunctionReturn(PETSC_SUCCESS);
5697 }
5698 
5699 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5700 {
5701   PetscDS  prob;
5702   PetscInt depth, Nf, h;
5703   DMLabel  label;
5704 
5705   PetscFunctionBeginHot;
5706   PetscCall(DMGetDS(dm, &prob));
5707   Nf      = prob->Nf;
5708   label   = dm->depthLabel;
5709   *dspace = NULL;
5710   if (field < Nf) {
5711     PetscObject disc = prob->disc[field];
5712 
5713     if (disc->classid == PETSCFE_CLASSID) {
5714       PetscDualSpace dsp;
5715 
5716       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5717       PetscCall(DMLabelGetNumValues(label, &depth));
5718       PetscCall(DMLabelGetValue(label, point, &h));
5719       h = depth - 1 - h;
5720       if (h) {
5721         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5722       } else {
5723         *dspace = dsp;
5724       }
5725     }
5726   }
5727   PetscFunctionReturn(PETSC_SUCCESS);
5728 }
5729 
5730 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5731 {
5732   PetscScalar       *array;
5733   const PetscScalar *vArray;
5734   const PetscInt    *cone, *coneO;
5735   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5736 
5737   PetscFunctionBeginHot;
5738   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5739   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5740   PetscCall(DMPlexGetCone(dm, point, &cone));
5741   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5742   if (!values || !*values) {
5743     if ((point >= pStart) && (point < pEnd)) {
5744       PetscInt dof;
5745 
5746       PetscCall(PetscSectionGetDof(section, point, &dof));
5747       size += dof;
5748     }
5749     for (p = 0; p < numPoints; ++p) {
5750       const PetscInt cp = cone[p];
5751       PetscInt       dof;
5752 
5753       if ((cp < pStart) || (cp >= pEnd)) continue;
5754       PetscCall(PetscSectionGetDof(section, cp, &dof));
5755       size += dof;
5756     }
5757     if (!values) {
5758       if (csize) *csize = size;
5759       PetscFunctionReturn(PETSC_SUCCESS);
5760     }
5761     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5762   } else {
5763     array = *values;
5764   }
5765   size = 0;
5766   PetscCall(VecGetArrayRead(v, &vArray));
5767   if ((point >= pStart) && (point < pEnd)) {
5768     PetscInt           dof, off, d;
5769     const PetscScalar *varr;
5770 
5771     PetscCall(PetscSectionGetDof(section, point, &dof));
5772     PetscCall(PetscSectionGetOffset(section, point, &off));
5773     varr = &vArray[off];
5774     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5775     size += dof;
5776   }
5777   for (p = 0; p < numPoints; ++p) {
5778     const PetscInt     cp = cone[p];
5779     PetscInt           o  = coneO[p];
5780     PetscInt           dof, off, d;
5781     const PetscScalar *varr;
5782 
5783     if ((cp < pStart) || (cp >= pEnd)) continue;
5784     PetscCall(PetscSectionGetDof(section, cp, &dof));
5785     PetscCall(PetscSectionGetOffset(section, cp, &off));
5786     varr = &vArray[off];
5787     if (o >= 0) {
5788       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5789     } else {
5790       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5791     }
5792     size += dof;
5793   }
5794   PetscCall(VecRestoreArrayRead(v, &vArray));
5795   if (!*values) {
5796     if (csize) *csize = size;
5797     *values = array;
5798   } else {
5799     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5800     *csize = size;
5801   }
5802   PetscFunctionReturn(PETSC_SUCCESS);
5803 }
5804 
5805 /* Compress out points not in the section */
5806 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5807 {
5808   const PetscInt np = *numPoints;
5809   PetscInt       pStart, pEnd, p, q;
5810 
5811   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5812   for (p = 0, q = 0; p < np; ++p) {
5813     const PetscInt r = points[p * 2];
5814     if ((r >= pStart) && (r < pEnd)) {
5815       points[q * 2]     = r;
5816       points[q * 2 + 1] = points[p * 2 + 1];
5817       ++q;
5818     }
5819   }
5820   *numPoints = q;
5821   return PETSC_SUCCESS;
5822 }
5823 
5824 /* Compressed closure does not apply closure permutation */
5825 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5826 {
5827   const PetscInt *cla = NULL;
5828   PetscInt        np, *pts = NULL;
5829 
5830   PetscFunctionBeginHot;
5831   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5832   if (*clPoints) {
5833     PetscInt dof, off;
5834 
5835     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5836     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5837     PetscCall(ISGetIndices(*clPoints, &cla));
5838     np  = dof / 2;
5839     pts = (PetscInt *)&cla[off];
5840   } else {
5841     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5842     PetscCall(CompressPoints_Private(section, &np, pts));
5843   }
5844   *numPoints = np;
5845   *points    = pts;
5846   *clp       = cla;
5847   PetscFunctionReturn(PETSC_SUCCESS);
5848 }
5849 
5850 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5851 {
5852   PetscFunctionBeginHot;
5853   if (!*clPoints) {
5854     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5855   } else {
5856     PetscCall(ISRestoreIndices(*clPoints, clp));
5857   }
5858   *numPoints = 0;
5859   *points    = NULL;
5860   *clSec     = NULL;
5861   *clPoints  = NULL;
5862   *clp       = NULL;
5863   PetscFunctionReturn(PETSC_SUCCESS);
5864 }
5865 
5866 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5867 {
5868   PetscInt            offset = 0, p;
5869   const PetscInt    **perms  = NULL;
5870   const PetscScalar **flips  = NULL;
5871 
5872   PetscFunctionBeginHot;
5873   *size = 0;
5874   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5875   for (p = 0; p < numPoints; p++) {
5876     const PetscInt     point = points[2 * p];
5877     const PetscInt    *perm  = perms ? perms[p] : NULL;
5878     const PetscScalar *flip  = flips ? flips[p] : NULL;
5879     PetscInt           dof, off, d;
5880     const PetscScalar *varr;
5881 
5882     PetscCall(PetscSectionGetDof(section, point, &dof));
5883     PetscCall(PetscSectionGetOffset(section, point, &off));
5884     varr = &vArray[off];
5885     if (clperm) {
5886       if (perm) {
5887         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5888       } else {
5889         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5890       }
5891       if (flip) {
5892         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5893       }
5894     } else {
5895       if (perm) {
5896         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5897       } else {
5898         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5899       }
5900       if (flip) {
5901         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5902       }
5903     }
5904     offset += dof;
5905   }
5906   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5907   *size = offset;
5908   PetscFunctionReturn(PETSC_SUCCESS);
5909 }
5910 
5911 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[])
5912 {
5913   PetscInt offset = 0, f;
5914 
5915   PetscFunctionBeginHot;
5916   *size = 0;
5917   for (f = 0; f < numFields; ++f) {
5918     PetscInt            p;
5919     const PetscInt    **perms = NULL;
5920     const PetscScalar **flips = NULL;
5921 
5922     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5923     for (p = 0; p < numPoints; p++) {
5924       const PetscInt     point = points[2 * p];
5925       PetscInt           fdof, foff, b;
5926       const PetscScalar *varr;
5927       const PetscInt    *perm = perms ? perms[p] : NULL;
5928       const PetscScalar *flip = flips ? flips[p] : NULL;
5929 
5930       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5931       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5932       varr = &vArray[foff];
5933       if (clperm) {
5934         if (perm) {
5935           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5936         } else {
5937           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5938         }
5939         if (flip) {
5940           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5941         }
5942       } else {
5943         if (perm) {
5944           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5945         } else {
5946           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5947         }
5948         if (flip) {
5949           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5950         }
5951       }
5952       offset += fdof;
5953     }
5954     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5955   }
5956   *size = offset;
5957   PetscFunctionReturn(PETSC_SUCCESS);
5958 }
5959 
5960 /*@C
5961   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5962 
5963   Not collective
5964 
5965   Input Parameters:
5966 + dm - The `DM`
5967 . section - The section describing the layout in v, or NULL to use the default section
5968 . v - The local vector
5969 - point - The point in the `DM`
5970 
5971   Input/Output Parameters:
5972 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5973 - values - An array to use for the values, or NULL to have it allocated automatically;
5974            if the user provided NULL, it is a borrowed array and should not be freed
5975 
5976   Level: intermediate
5977 
5978   Notes:
5979   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to NULL in the
5980   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
5981   assembly function, and a user may already have allocated storage for this operation.
5982 
5983   A typical use could be
5984 .vb
5985    values = NULL;
5986    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5987    for (cl = 0; cl < clSize; ++cl) {
5988      <Compute on closure>
5989    }
5990    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5991 .ve
5992   or
5993 .vb
5994    PetscMalloc1(clMaxSize, &values);
5995    for (p = pStart; p < pEnd; ++p) {
5996      clSize = clMaxSize;
5997      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5998      for (cl = 0; cl < clSize; ++cl) {
5999        <Compute on closure>
6000      }
6001    }
6002    PetscFree(values);
6003 .ve
6004 
6005   Fortran Note:
6006   The csize argument is not present in the Fortran binding since it is internal to the array.
6007 
6008 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6009 @*/
6010 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6011 {
6012   PetscSection    clSection;
6013   IS              clPoints;
6014   PetscInt       *points = NULL;
6015   const PetscInt *clp, *perm;
6016   PetscInt        depth, numFields, numPoints, asize;
6017 
6018   PetscFunctionBeginHot;
6019   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6020   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6021   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6022   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6023   PetscCall(DMPlexGetDepth(dm, &depth));
6024   PetscCall(PetscSectionGetNumFields(section, &numFields));
6025   if (depth == 1 && numFields < 2) {
6026     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6027     PetscFunctionReturn(PETSC_SUCCESS);
6028   }
6029   /* Get points */
6030   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6031   /* Get sizes */
6032   asize = 0;
6033   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6034     PetscInt dof;
6035     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6036     asize += dof;
6037   }
6038   if (values) {
6039     const PetscScalar *vArray;
6040     PetscInt           size;
6041 
6042     if (*values) {
6043       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);
6044     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6045     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6046     PetscCall(VecGetArrayRead(v, &vArray));
6047     /* Get values */
6048     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6049     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6050     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6051     /* Cleanup array */
6052     PetscCall(VecRestoreArrayRead(v, &vArray));
6053   }
6054   if (csize) *csize = asize;
6055   /* Cleanup points */
6056   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6057   PetscFunctionReturn(PETSC_SUCCESS);
6058 }
6059 
6060 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6061 {
6062   DMLabel            depthLabel;
6063   PetscSection       clSection;
6064   IS                 clPoints;
6065   PetscScalar       *array;
6066   const PetscScalar *vArray;
6067   PetscInt          *points = NULL;
6068   const PetscInt    *clp, *perm = NULL;
6069   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6070 
6071   PetscFunctionBeginHot;
6072   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6073   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6074   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6075   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6076   PetscCall(DMPlexGetDepth(dm, &mdepth));
6077   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6078   PetscCall(PetscSectionGetNumFields(section, &numFields));
6079   if (mdepth == 1 && numFields < 2) {
6080     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6081     PetscFunctionReturn(PETSC_SUCCESS);
6082   }
6083   /* Get points */
6084   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6085   for (clsize = 0, p = 0; p < Np; p++) {
6086     PetscInt dof;
6087     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6088     clsize += dof;
6089   }
6090   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6091   /* Filter points */
6092   for (p = 0; p < numPoints * 2; p += 2) {
6093     PetscInt dep;
6094 
6095     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6096     if (dep != depth) continue;
6097     points[Np * 2 + 0] = points[p];
6098     points[Np * 2 + 1] = points[p + 1];
6099     ++Np;
6100   }
6101   /* Get array */
6102   if (!values || !*values) {
6103     PetscInt asize = 0, dof;
6104 
6105     for (p = 0; p < Np * 2; p += 2) {
6106       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6107       asize += dof;
6108     }
6109     if (!values) {
6110       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6111       if (csize) *csize = asize;
6112       PetscFunctionReturn(PETSC_SUCCESS);
6113     }
6114     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6115   } else {
6116     array = *values;
6117   }
6118   PetscCall(VecGetArrayRead(v, &vArray));
6119   /* Get values */
6120   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6121   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6122   /* Cleanup points */
6123   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6124   /* Cleanup array */
6125   PetscCall(VecRestoreArrayRead(v, &vArray));
6126   if (!*values) {
6127     if (csize) *csize = size;
6128     *values = array;
6129   } else {
6130     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6131     *csize = size;
6132   }
6133   PetscFunctionReturn(PETSC_SUCCESS);
6134 }
6135 
6136 /*@C
6137   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6138 
6139   Not collective
6140 
6141   Input Parameters:
6142 + dm - The `DM`
6143 . section - The section describing the layout in v, or NULL to use the default section
6144 . v - The local vector
6145 . point - The point in the `DM`
6146 . csize - The number of values in the closure, or NULL
6147 - values - The array of values, which is a borrowed array and should not be freed
6148 
6149   Level: intermediate
6150 
6151   Note:
6152   The array values are discarded and not copied back into v. In order to copy values back to v, use `DMPlexVecSetClosure()`
6153 
6154   Fortran Note:
6155   The csize argument is not present in the Fortran binding since it is internal to the array.
6156 
6157 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6158 @*/
6159 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6160 {
6161   PetscInt size = 0;
6162 
6163   PetscFunctionBegin;
6164   /* Should work without recalculating size */
6165   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6166   *values = NULL;
6167   PetscFunctionReturn(PETSC_SUCCESS);
6168 }
6169 
6170 static inline void add(PetscScalar *x, PetscScalar y)
6171 {
6172   *x += y;
6173 }
6174 static inline void insert(PetscScalar *x, PetscScalar y)
6175 {
6176   *x = y;
6177 }
6178 
6179 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[])
6180 {
6181   PetscInt        cdof;  /* The number of constraints on this point */
6182   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6183   PetscScalar    *a;
6184   PetscInt        off, cind = 0, k;
6185 
6186   PetscFunctionBegin;
6187   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6188   PetscCall(PetscSectionGetOffset(section, point, &off));
6189   a = &array[off];
6190   if (!cdof || setBC) {
6191     if (clperm) {
6192       if (perm) {
6193         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6194       } else {
6195         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6196       }
6197     } else {
6198       if (perm) {
6199         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6200       } else {
6201         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6202       }
6203     }
6204   } else {
6205     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6206     if (clperm) {
6207       if (perm) {
6208         for (k = 0; k < dof; ++k) {
6209           if ((cind < cdof) && (k == cdofs[cind])) {
6210             ++cind;
6211             continue;
6212           }
6213           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6214         }
6215       } else {
6216         for (k = 0; k < dof; ++k) {
6217           if ((cind < cdof) && (k == cdofs[cind])) {
6218             ++cind;
6219             continue;
6220           }
6221           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6222         }
6223       }
6224     } else {
6225       if (perm) {
6226         for (k = 0; k < dof; ++k) {
6227           if ((cind < cdof) && (k == cdofs[cind])) {
6228             ++cind;
6229             continue;
6230           }
6231           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6232         }
6233       } else {
6234         for (k = 0; k < dof; ++k) {
6235           if ((cind < cdof) && (k == cdofs[cind])) {
6236             ++cind;
6237             continue;
6238           }
6239           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6240         }
6241       }
6242     }
6243   }
6244   PetscFunctionReturn(PETSC_SUCCESS);
6245 }
6246 
6247 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[])
6248 {
6249   PetscInt        cdof;  /* The number of constraints on this point */
6250   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6251   PetscScalar    *a;
6252   PetscInt        off, cind = 0, k;
6253 
6254   PetscFunctionBegin;
6255   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6256   PetscCall(PetscSectionGetOffset(section, point, &off));
6257   a = &array[off];
6258   if (cdof) {
6259     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6260     if (clperm) {
6261       if (perm) {
6262         for (k = 0; k < dof; ++k) {
6263           if ((cind < cdof) && (k == cdofs[cind])) {
6264             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6265             cind++;
6266           }
6267         }
6268       } else {
6269         for (k = 0; k < dof; ++k) {
6270           if ((cind < cdof) && (k == cdofs[cind])) {
6271             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6272             cind++;
6273           }
6274         }
6275       }
6276     } else {
6277       if (perm) {
6278         for (k = 0; k < dof; ++k) {
6279           if ((cind < cdof) && (k == cdofs[cind])) {
6280             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6281             cind++;
6282           }
6283         }
6284       } else {
6285         for (k = 0; k < dof; ++k) {
6286           if ((cind < cdof) && (k == cdofs[cind])) {
6287             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6288             cind++;
6289           }
6290         }
6291       }
6292     }
6293   }
6294   PetscFunctionReturn(PETSC_SUCCESS);
6295 }
6296 
6297 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[])
6298 {
6299   PetscScalar    *a;
6300   PetscInt        fdof, foff, fcdof, foffset = *offset;
6301   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6302   PetscInt        cind = 0, b;
6303 
6304   PetscFunctionBegin;
6305   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6306   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6307   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6308   a = &array[foff];
6309   if (!fcdof || setBC) {
6310     if (clperm) {
6311       if (perm) {
6312         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6313       } else {
6314         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6315       }
6316     } else {
6317       if (perm) {
6318         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6319       } else {
6320         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6321       }
6322     }
6323   } else {
6324     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6325     if (clperm) {
6326       if (perm) {
6327         for (b = 0; b < fdof; b++) {
6328           if ((cind < fcdof) && (b == fcdofs[cind])) {
6329             ++cind;
6330             continue;
6331           }
6332           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6333         }
6334       } else {
6335         for (b = 0; b < fdof; b++) {
6336           if ((cind < fcdof) && (b == fcdofs[cind])) {
6337             ++cind;
6338             continue;
6339           }
6340           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6341         }
6342       }
6343     } else {
6344       if (perm) {
6345         for (b = 0; b < fdof; b++) {
6346           if ((cind < fcdof) && (b == fcdofs[cind])) {
6347             ++cind;
6348             continue;
6349           }
6350           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6351         }
6352       } else {
6353         for (b = 0; b < fdof; b++) {
6354           if ((cind < fcdof) && (b == fcdofs[cind])) {
6355             ++cind;
6356             continue;
6357           }
6358           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6359         }
6360       }
6361     }
6362   }
6363   *offset += fdof;
6364   PetscFunctionReturn(PETSC_SUCCESS);
6365 }
6366 
6367 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[])
6368 {
6369   PetscScalar    *a;
6370   PetscInt        fdof, foff, fcdof, foffset = *offset;
6371   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6372   PetscInt        Nc, cind = 0, ncind = 0, b;
6373   PetscBool       ncSet, fcSet;
6374 
6375   PetscFunctionBegin;
6376   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6377   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6378   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6379   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6380   a = &array[foff];
6381   if (fcdof) {
6382     /* We just override fcdof and fcdofs with Ncc and comps */
6383     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6384     if (clperm) {
6385       if (perm) {
6386         if (comps) {
6387           for (b = 0; b < fdof; b++) {
6388             ncSet = fcSet = PETSC_FALSE;
6389             if (b % Nc == comps[ncind]) {
6390               ncind = (ncind + 1) % Ncc;
6391               ncSet = PETSC_TRUE;
6392             }
6393             if ((cind < fcdof) && (b == fcdofs[cind])) {
6394               ++cind;
6395               fcSet = PETSC_TRUE;
6396             }
6397             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6398           }
6399         } else {
6400           for (b = 0; b < fdof; b++) {
6401             if ((cind < fcdof) && (b == fcdofs[cind])) {
6402               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6403               ++cind;
6404             }
6405           }
6406         }
6407       } else {
6408         if (comps) {
6409           for (b = 0; b < fdof; b++) {
6410             ncSet = fcSet = PETSC_FALSE;
6411             if (b % Nc == comps[ncind]) {
6412               ncind = (ncind + 1) % Ncc;
6413               ncSet = PETSC_TRUE;
6414             }
6415             if ((cind < fcdof) && (b == fcdofs[cind])) {
6416               ++cind;
6417               fcSet = PETSC_TRUE;
6418             }
6419             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6420           }
6421         } else {
6422           for (b = 0; b < fdof; b++) {
6423             if ((cind < fcdof) && (b == fcdofs[cind])) {
6424               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6425               ++cind;
6426             }
6427           }
6428         }
6429       }
6430     } else {
6431       if (perm) {
6432         if (comps) {
6433           for (b = 0; b < fdof; b++) {
6434             ncSet = fcSet = PETSC_FALSE;
6435             if (b % Nc == comps[ncind]) {
6436               ncind = (ncind + 1) % Ncc;
6437               ncSet = PETSC_TRUE;
6438             }
6439             if ((cind < fcdof) && (b == fcdofs[cind])) {
6440               ++cind;
6441               fcSet = PETSC_TRUE;
6442             }
6443             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6444           }
6445         } else {
6446           for (b = 0; b < fdof; b++) {
6447             if ((cind < fcdof) && (b == fcdofs[cind])) {
6448               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6449               ++cind;
6450             }
6451           }
6452         }
6453       } else {
6454         if (comps) {
6455           for (b = 0; b < fdof; b++) {
6456             ncSet = fcSet = PETSC_FALSE;
6457             if (b % Nc == comps[ncind]) {
6458               ncind = (ncind + 1) % Ncc;
6459               ncSet = PETSC_TRUE;
6460             }
6461             if ((cind < fcdof) && (b == fcdofs[cind])) {
6462               ++cind;
6463               fcSet = PETSC_TRUE;
6464             }
6465             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6466           }
6467         } else {
6468           for (b = 0; b < fdof; b++) {
6469             if ((cind < fcdof) && (b == fcdofs[cind])) {
6470               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6471               ++cind;
6472             }
6473           }
6474         }
6475       }
6476     }
6477   }
6478   *offset += fdof;
6479   PetscFunctionReturn(PETSC_SUCCESS);
6480 }
6481 
6482 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6483 {
6484   PetscScalar    *array;
6485   const PetscInt *cone, *coneO;
6486   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6487 
6488   PetscFunctionBeginHot;
6489   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6490   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6491   PetscCall(DMPlexGetCone(dm, point, &cone));
6492   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6493   PetscCall(VecGetArray(v, &array));
6494   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6495     const PetscInt cp = !p ? point : cone[p - 1];
6496     const PetscInt o  = !p ? 0 : coneO[p - 1];
6497 
6498     if ((cp < pStart) || (cp >= pEnd)) {
6499       dof = 0;
6500       continue;
6501     }
6502     PetscCall(PetscSectionGetDof(section, cp, &dof));
6503     /* ADD_VALUES */
6504     {
6505       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6506       PetscScalar    *a;
6507       PetscInt        cdof, coff, cind = 0, k;
6508 
6509       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6510       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6511       a = &array[coff];
6512       if (!cdof) {
6513         if (o >= 0) {
6514           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6515         } else {
6516           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6517         }
6518       } else {
6519         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6520         if (o >= 0) {
6521           for (k = 0; k < dof; ++k) {
6522             if ((cind < cdof) && (k == cdofs[cind])) {
6523               ++cind;
6524               continue;
6525             }
6526             a[k] += values[off + k];
6527           }
6528         } else {
6529           for (k = 0; k < dof; ++k) {
6530             if ((cind < cdof) && (k == cdofs[cind])) {
6531               ++cind;
6532               continue;
6533             }
6534             a[k] += values[off + dof - k - 1];
6535           }
6536         }
6537       }
6538     }
6539   }
6540   PetscCall(VecRestoreArray(v, &array));
6541   PetscFunctionReturn(PETSC_SUCCESS);
6542 }
6543 
6544 /*@C
6545   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6546 
6547   Not collective
6548 
6549   Input Parameters:
6550 + dm - The `DM`
6551 . section - The section describing the layout in v, or NULL to use the default section
6552 . v - The local vector
6553 . point - The point in the DM
6554 . values - The array of values
6555 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6556          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6557 
6558   Level: intermediate
6559 
6560 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6561 @*/
6562 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6563 {
6564   PetscSection    clSection;
6565   IS              clPoints;
6566   PetscScalar    *array;
6567   PetscInt       *points = NULL;
6568   const PetscInt *clp, *clperm = NULL;
6569   PetscInt        depth, numFields, numPoints, p, clsize;
6570 
6571   PetscFunctionBeginHot;
6572   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6573   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6574   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6575   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6576   PetscCall(DMPlexGetDepth(dm, &depth));
6577   PetscCall(PetscSectionGetNumFields(section, &numFields));
6578   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6579     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6580     PetscFunctionReturn(PETSC_SUCCESS);
6581   }
6582   /* Get points */
6583   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6584   for (clsize = 0, p = 0; p < numPoints; p++) {
6585     PetscInt dof;
6586     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6587     clsize += dof;
6588   }
6589   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6590   /* Get array */
6591   PetscCall(VecGetArray(v, &array));
6592   /* Get values */
6593   if (numFields > 0) {
6594     PetscInt offset = 0, f;
6595     for (f = 0; f < numFields; ++f) {
6596       const PetscInt    **perms = NULL;
6597       const PetscScalar **flips = NULL;
6598 
6599       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6600       switch (mode) {
6601       case INSERT_VALUES:
6602         for (p = 0; p < numPoints; p++) {
6603           const PetscInt     point = points[2 * p];
6604           const PetscInt    *perm  = perms ? perms[p] : NULL;
6605           const PetscScalar *flip  = flips ? flips[p] : NULL;
6606           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6607         }
6608         break;
6609       case INSERT_ALL_VALUES:
6610         for (p = 0; p < numPoints; p++) {
6611           const PetscInt     point = points[2 * p];
6612           const PetscInt    *perm  = perms ? perms[p] : NULL;
6613           const PetscScalar *flip  = flips ? flips[p] : NULL;
6614           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6615         }
6616         break;
6617       case INSERT_BC_VALUES:
6618         for (p = 0; p < numPoints; p++) {
6619           const PetscInt     point = points[2 * p];
6620           const PetscInt    *perm  = perms ? perms[p] : NULL;
6621           const PetscScalar *flip  = flips ? flips[p] : NULL;
6622           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6623         }
6624         break;
6625       case ADD_VALUES:
6626         for (p = 0; p < numPoints; p++) {
6627           const PetscInt     point = points[2 * p];
6628           const PetscInt    *perm  = perms ? perms[p] : NULL;
6629           const PetscScalar *flip  = flips ? flips[p] : NULL;
6630           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6631         }
6632         break;
6633       case ADD_ALL_VALUES:
6634         for (p = 0; p < numPoints; p++) {
6635           const PetscInt     point = points[2 * p];
6636           const PetscInt    *perm  = perms ? perms[p] : NULL;
6637           const PetscScalar *flip  = flips ? flips[p] : NULL;
6638           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6639         }
6640         break;
6641       case ADD_BC_VALUES:
6642         for (p = 0; p < numPoints; p++) {
6643           const PetscInt     point = points[2 * p];
6644           const PetscInt    *perm  = perms ? perms[p] : NULL;
6645           const PetscScalar *flip  = flips ? flips[p] : NULL;
6646           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6647         }
6648         break;
6649       default:
6650         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6651       }
6652       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6653     }
6654   } else {
6655     PetscInt            dof, off;
6656     const PetscInt    **perms = NULL;
6657     const PetscScalar **flips = NULL;
6658 
6659     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6660     switch (mode) {
6661     case INSERT_VALUES:
6662       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6663         const PetscInt     point = points[2 * p];
6664         const PetscInt    *perm  = perms ? perms[p] : NULL;
6665         const PetscScalar *flip  = flips ? flips[p] : NULL;
6666         PetscCall(PetscSectionGetDof(section, point, &dof));
6667         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6668       }
6669       break;
6670     case INSERT_ALL_VALUES:
6671       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6672         const PetscInt     point = points[2 * p];
6673         const PetscInt    *perm  = perms ? perms[p] : NULL;
6674         const PetscScalar *flip  = flips ? flips[p] : NULL;
6675         PetscCall(PetscSectionGetDof(section, point, &dof));
6676         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6677       }
6678       break;
6679     case INSERT_BC_VALUES:
6680       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6681         const PetscInt     point = points[2 * p];
6682         const PetscInt    *perm  = perms ? perms[p] : NULL;
6683         const PetscScalar *flip  = flips ? flips[p] : NULL;
6684         PetscCall(PetscSectionGetDof(section, point, &dof));
6685         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6686       }
6687       break;
6688     case ADD_VALUES:
6689       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6690         const PetscInt     point = points[2 * p];
6691         const PetscInt    *perm  = perms ? perms[p] : NULL;
6692         const PetscScalar *flip  = flips ? flips[p] : NULL;
6693         PetscCall(PetscSectionGetDof(section, point, &dof));
6694         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6695       }
6696       break;
6697     case ADD_ALL_VALUES:
6698       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6699         const PetscInt     point = points[2 * p];
6700         const PetscInt    *perm  = perms ? perms[p] : NULL;
6701         const PetscScalar *flip  = flips ? flips[p] : NULL;
6702         PetscCall(PetscSectionGetDof(section, point, &dof));
6703         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6704       }
6705       break;
6706     case ADD_BC_VALUES:
6707       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6708         const PetscInt     point = points[2 * p];
6709         const PetscInt    *perm  = perms ? perms[p] : NULL;
6710         const PetscScalar *flip  = flips ? flips[p] : NULL;
6711         PetscCall(PetscSectionGetDof(section, point, &dof));
6712         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6713       }
6714       break;
6715     default:
6716       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6717     }
6718     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6719   }
6720   /* Cleanup points */
6721   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6722   /* Cleanup array */
6723   PetscCall(VecRestoreArray(v, &array));
6724   PetscFunctionReturn(PETSC_SUCCESS);
6725 }
6726 
6727 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6728 {
6729   const PetscInt *supp, *cone;
6730   PetscScalar    *a;
6731   PetscInt        dim, Ns, dof, off, n = 0;
6732 
6733   PetscFunctionBegin;
6734   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6735   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6736   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6737   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6738   if (PetscDefined(USE_DEBUG)) {
6739     PetscInt vStart, vEnd;
6740 
6741     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6742     PetscCheck(point >= vStart && point < vEnd, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " must be a vertex in [%" PetscInt_FMT ", %" PetscInt_FMT "]", point, vStart, vEnd);
6743   }
6744   PetscValidScalarPointer(values, 5);
6745 
6746   PetscCall(DMGetDimension(dm, &dim));
6747   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6748   PetscCall(DMPlexGetSupport(dm, point, &supp));
6749   PetscCheck(Ns == 2 * dim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " has support size %" PetscInt_FMT " != %" PetscInt_FMT, point, Ns, 2 * dim);
6750   PetscCall(VecGetArray(v, &a));
6751   PetscCall(PetscSectionGetDof(section, point, &dof));
6752   PetscCall(PetscSectionGetOffset(section, point, &off));
6753   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6754   for (PetscInt d = 0; d < dim; ++d) {
6755     // Left edge
6756     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6757     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6758     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6759     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6760     // Right edge
6761     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6762     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6763     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6764     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6765   }
6766   PetscCall(VecRestoreArray(v, &a));
6767   PetscFunctionReturn(PETSC_SUCCESS);
6768 }
6769 
6770 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6771 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6772 {
6773   PetscFunctionBegin;
6774   *contains = PETSC_TRUE;
6775   if (label) {
6776     PetscInt fdof;
6777 
6778     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6779     if (!*contains) {
6780       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6781       *offset += fdof;
6782       PetscFunctionReturn(PETSC_SUCCESS);
6783     }
6784   }
6785   PetscFunctionReturn(PETSC_SUCCESS);
6786 }
6787 
6788 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6789 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)
6790 {
6791   PetscSection    clSection;
6792   IS              clPoints;
6793   PetscScalar    *array;
6794   PetscInt       *points = NULL;
6795   const PetscInt *clp;
6796   PetscInt        numFields, numPoints, p;
6797   PetscInt        offset = 0, f;
6798 
6799   PetscFunctionBeginHot;
6800   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6801   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6802   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6803   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6804   PetscCall(PetscSectionGetNumFields(section, &numFields));
6805   /* Get points */
6806   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6807   /* Get array */
6808   PetscCall(VecGetArray(v, &array));
6809   /* Get values */
6810   for (f = 0; f < numFields; ++f) {
6811     const PetscInt    **perms = NULL;
6812     const PetscScalar **flips = NULL;
6813     PetscBool           contains;
6814 
6815     if (!fieldActive[f]) {
6816       for (p = 0; p < numPoints * 2; p += 2) {
6817         PetscInt fdof;
6818         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6819         offset += fdof;
6820       }
6821       continue;
6822     }
6823     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6824     switch (mode) {
6825     case INSERT_VALUES:
6826       for (p = 0; p < numPoints; p++) {
6827         const PetscInt     point = points[2 * p];
6828         const PetscInt    *perm  = perms ? perms[p] : NULL;
6829         const PetscScalar *flip  = flips ? flips[p] : NULL;
6830         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6831         if (!contains) continue;
6832         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6833       }
6834       break;
6835     case INSERT_ALL_VALUES:
6836       for (p = 0; p < numPoints; p++) {
6837         const PetscInt     point = points[2 * p];
6838         const PetscInt    *perm  = perms ? perms[p] : NULL;
6839         const PetscScalar *flip  = flips ? flips[p] : NULL;
6840         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6841         if (!contains) continue;
6842         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6843       }
6844       break;
6845     case INSERT_BC_VALUES:
6846       for (p = 0; p < numPoints; p++) {
6847         const PetscInt     point = points[2 * p];
6848         const PetscInt    *perm  = perms ? perms[p] : NULL;
6849         const PetscScalar *flip  = flips ? flips[p] : NULL;
6850         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6851         if (!contains) continue;
6852         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6853       }
6854       break;
6855     case ADD_VALUES:
6856       for (p = 0; p < numPoints; p++) {
6857         const PetscInt     point = points[2 * p];
6858         const PetscInt    *perm  = perms ? perms[p] : NULL;
6859         const PetscScalar *flip  = flips ? flips[p] : NULL;
6860         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6861         if (!contains) continue;
6862         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6863       }
6864       break;
6865     case ADD_ALL_VALUES:
6866       for (p = 0; p < numPoints; p++) {
6867         const PetscInt     point = points[2 * p];
6868         const PetscInt    *perm  = perms ? perms[p] : NULL;
6869         const PetscScalar *flip  = flips ? flips[p] : NULL;
6870         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6871         if (!contains) continue;
6872         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6873       }
6874       break;
6875     default:
6876       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6877     }
6878     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6879   }
6880   /* Cleanup points */
6881   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6882   /* Cleanup array */
6883   PetscCall(VecRestoreArray(v, &array));
6884   PetscFunctionReturn(PETSC_SUCCESS);
6885 }
6886 
6887 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6888 {
6889   PetscMPIInt rank;
6890   PetscInt    i, j;
6891 
6892   PetscFunctionBegin;
6893   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6894   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6895   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6896   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6897   numCIndices = numCIndices ? numCIndices : numRIndices;
6898   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6899   for (i = 0; i < numRIndices; i++) {
6900     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6901     for (j = 0; j < numCIndices; j++) {
6902 #if defined(PETSC_USE_COMPLEX)
6903       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6904 #else
6905       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6906 #endif
6907     }
6908     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6909   }
6910   PetscFunctionReturn(PETSC_SUCCESS);
6911 }
6912 
6913 /*
6914   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6915 
6916   Input Parameters:
6917 + section - The section for this data layout
6918 . islocal - Is the section (and thus indices being requested) local or global?
6919 . point   - The point contributing dofs with these indices
6920 . off     - The global offset of this point
6921 . loff    - The local offset of each field
6922 . setBC   - The flag determining whether to include indices of boundary values
6923 . perm    - A permutation of the dofs on this point, or NULL
6924 - indperm - A permutation of the entire indices array, or NULL
6925 
6926   Output Parameter:
6927 . indices - Indices for dofs on this point
6928 
6929   Level: developer
6930 
6931   Note: The indices could be local or global, depending on the value of 'off'.
6932 */
6933 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6934 {
6935   PetscInt        dof;   /* The number of unknowns on this point */
6936   PetscInt        cdof;  /* The number of constraints on this point */
6937   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6938   PetscInt        cind = 0, k;
6939 
6940   PetscFunctionBegin;
6941   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6942   PetscCall(PetscSectionGetDof(section, point, &dof));
6943   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6944   if (!cdof || setBC) {
6945     for (k = 0; k < dof; ++k) {
6946       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6947       const PetscInt ind    = indperm ? indperm[preind] : preind;
6948 
6949       indices[ind] = off + k;
6950     }
6951   } else {
6952     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6953     for (k = 0; k < dof; ++k) {
6954       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6955       const PetscInt ind    = indperm ? indperm[preind] : preind;
6956 
6957       if ((cind < cdof) && (k == cdofs[cind])) {
6958         /* Insert check for returning constrained indices */
6959         indices[ind] = -(off + k + 1);
6960         ++cind;
6961       } else {
6962         indices[ind] = off + k - (islocal ? 0 : cind);
6963       }
6964     }
6965   }
6966   *loff += dof;
6967   PetscFunctionReturn(PETSC_SUCCESS);
6968 }
6969 
6970 /*
6971  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6972 
6973  Input Parameters:
6974 + section - a section (global or local)
6975 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6976 . point - point within section
6977 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6978 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6979 . setBC - identify constrained (boundary condition) points via involution.
6980 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6981 . permsoff - offset
6982 - indperm - index permutation
6983 
6984  Output Parameter:
6985 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6986 . indices - array to hold indices (as defined by section) of each dof associated with point
6987 
6988  Notes:
6989  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6990  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6991  in the local vector.
6992 
6993  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6994  significant).  It is invalid to call with a global section and setBC=true.
6995 
6996  Developer Note:
6997  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6998  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6999  offset could be obtained from the section instead of passing it explicitly as we do now.
7000 
7001  Example:
7002  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7003  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7004  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7005  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.
7006 
7007  Level: developer
7008 */
7009 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[])
7010 {
7011   PetscInt numFields, foff, f;
7012 
7013   PetscFunctionBegin;
7014   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7015   PetscCall(PetscSectionGetNumFields(section, &numFields));
7016   for (f = 0, foff = 0; f < numFields; ++f) {
7017     PetscInt        fdof, cfdof;
7018     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7019     PetscInt        cind = 0, b;
7020     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7021 
7022     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7023     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7024     if (!cfdof || setBC) {
7025       for (b = 0; b < fdof; ++b) {
7026         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7027         const PetscInt ind    = indperm ? indperm[preind] : preind;
7028 
7029         indices[ind] = off + foff + b;
7030       }
7031     } else {
7032       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7033       for (b = 0; b < fdof; ++b) {
7034         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7035         const PetscInt ind    = indperm ? indperm[preind] : preind;
7036 
7037         if ((cind < cfdof) && (b == fcdofs[cind])) {
7038           indices[ind] = -(off + foff + b + 1);
7039           ++cind;
7040         } else {
7041           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7042         }
7043       }
7044     }
7045     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7046     foffs[f] += fdof;
7047   }
7048   PetscFunctionReturn(PETSC_SUCCESS);
7049 }
7050 
7051 /*
7052   This version believes the globalSection offsets for each field, rather than just the point offset
7053 
7054  . foffs - The offset into 'indices' for each field, since it is segregated by field
7055 
7056  Notes:
7057  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7058  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7059 */
7060 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7061 {
7062   PetscInt numFields, foff, f;
7063 
7064   PetscFunctionBegin;
7065   PetscCall(PetscSectionGetNumFields(section, &numFields));
7066   for (f = 0; f < numFields; ++f) {
7067     PetscInt        fdof, cfdof;
7068     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7069     PetscInt        cind = 0, b;
7070     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7071 
7072     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7073     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7074     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7075     if (!cfdof) {
7076       for (b = 0; b < fdof; ++b) {
7077         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7078         const PetscInt ind    = indperm ? indperm[preind] : preind;
7079 
7080         indices[ind] = foff + b;
7081       }
7082     } else {
7083       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7084       for (b = 0; b < fdof; ++b) {
7085         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7086         const PetscInt ind    = indperm ? indperm[preind] : preind;
7087 
7088         if ((cind < cfdof) && (b == fcdofs[cind])) {
7089           indices[ind] = -(foff + b + 1);
7090           ++cind;
7091         } else {
7092           indices[ind] = foff + b - cind;
7093         }
7094       }
7095     }
7096     foffs[f] += fdof;
7097   }
7098   PetscFunctionReturn(PETSC_SUCCESS);
7099 }
7100 
7101 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)
7102 {
7103   Mat             cMat;
7104   PetscSection    aSec, cSec;
7105   IS              aIS;
7106   PetscInt        aStart = -1, aEnd = -1;
7107   const PetscInt *anchors;
7108   PetscInt        numFields, f, p, q, newP = 0;
7109   PetscInt        newNumPoints = 0, newNumIndices = 0;
7110   PetscInt       *newPoints, *indices, *newIndices;
7111   PetscInt        maxAnchor, maxDof;
7112   PetscInt        newOffsets[32];
7113   PetscInt       *pointMatOffsets[32];
7114   PetscInt       *newPointOffsets[32];
7115   PetscScalar    *pointMat[32];
7116   PetscScalar    *newValues      = NULL, *tmpValues;
7117   PetscBool       anyConstrained = PETSC_FALSE;
7118 
7119   PetscFunctionBegin;
7120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7121   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7122   PetscCall(PetscSectionGetNumFields(section, &numFields));
7123 
7124   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7125   /* if there are point-to-point constraints */
7126   if (aSec) {
7127     PetscCall(PetscArrayzero(newOffsets, 32));
7128     PetscCall(ISGetIndices(aIS, &anchors));
7129     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7130     /* figure out how many points are going to be in the new element matrix
7131      * (we allow double counting, because it's all just going to be summed
7132      * into the global matrix anyway) */
7133     for (p = 0; p < 2 * numPoints; p += 2) {
7134       PetscInt b    = points[p];
7135       PetscInt bDof = 0, bSecDof;
7136 
7137       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7138       if (!bSecDof) continue;
7139       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7140       if (bDof) {
7141         /* this point is constrained */
7142         /* it is going to be replaced by its anchors */
7143         PetscInt bOff, q;
7144 
7145         anyConstrained = PETSC_TRUE;
7146         newNumPoints += bDof;
7147         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7148         for (q = 0; q < bDof; q++) {
7149           PetscInt a = anchors[bOff + q];
7150           PetscInt aDof;
7151 
7152           PetscCall(PetscSectionGetDof(section, a, &aDof));
7153           newNumIndices += aDof;
7154           for (f = 0; f < numFields; ++f) {
7155             PetscInt fDof;
7156 
7157             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7158             newOffsets[f + 1] += fDof;
7159           }
7160         }
7161       } else {
7162         /* this point is not constrained */
7163         newNumPoints++;
7164         newNumIndices += bSecDof;
7165         for (f = 0; f < numFields; ++f) {
7166           PetscInt fDof;
7167 
7168           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7169           newOffsets[f + 1] += fDof;
7170         }
7171       }
7172     }
7173   }
7174   if (!anyConstrained) {
7175     if (outNumPoints) *outNumPoints = 0;
7176     if (outNumIndices) *outNumIndices = 0;
7177     if (outPoints) *outPoints = NULL;
7178     if (outValues) *outValues = NULL;
7179     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7180     PetscFunctionReturn(PETSC_SUCCESS);
7181   }
7182 
7183   if (outNumPoints) *outNumPoints = newNumPoints;
7184   if (outNumIndices) *outNumIndices = newNumIndices;
7185 
7186   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7187 
7188   if (!outPoints && !outValues) {
7189     if (offsets) {
7190       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7191     }
7192     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7193     PetscFunctionReturn(PETSC_SUCCESS);
7194   }
7195 
7196   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7197 
7198   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7199 
7200   /* workspaces */
7201   if (numFields) {
7202     for (f = 0; f < numFields; f++) {
7203       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7204       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7205     }
7206   } else {
7207     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7208     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7209   }
7210 
7211   /* get workspaces for the point-to-point matrices */
7212   if (numFields) {
7213     PetscInt totalOffset, totalMatOffset;
7214 
7215     for (p = 0; p < numPoints; p++) {
7216       PetscInt b    = points[2 * p];
7217       PetscInt bDof = 0, bSecDof;
7218 
7219       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7220       if (!bSecDof) {
7221         for (f = 0; f < numFields; f++) {
7222           newPointOffsets[f][p + 1] = 0;
7223           pointMatOffsets[f][p + 1] = 0;
7224         }
7225         continue;
7226       }
7227       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7228       if (bDof) {
7229         for (f = 0; f < numFields; f++) {
7230           PetscInt fDof, q, bOff, allFDof = 0;
7231 
7232           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7233           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7234           for (q = 0; q < bDof; q++) {
7235             PetscInt a = anchors[bOff + q];
7236             PetscInt aFDof;
7237 
7238             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7239             allFDof += aFDof;
7240           }
7241           newPointOffsets[f][p + 1] = allFDof;
7242           pointMatOffsets[f][p + 1] = fDof * allFDof;
7243         }
7244       } else {
7245         for (f = 0; f < numFields; f++) {
7246           PetscInt fDof;
7247 
7248           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7249           newPointOffsets[f][p + 1] = fDof;
7250           pointMatOffsets[f][p + 1] = 0;
7251         }
7252       }
7253     }
7254     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7255       newPointOffsets[f][0] = totalOffset;
7256       pointMatOffsets[f][0] = totalMatOffset;
7257       for (p = 0; p < numPoints; p++) {
7258         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7259         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7260       }
7261       totalOffset    = newPointOffsets[f][numPoints];
7262       totalMatOffset = pointMatOffsets[f][numPoints];
7263       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7264     }
7265   } else {
7266     for (p = 0; p < numPoints; p++) {
7267       PetscInt b    = points[2 * p];
7268       PetscInt bDof = 0, bSecDof;
7269 
7270       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7271       if (!bSecDof) {
7272         newPointOffsets[0][p + 1] = 0;
7273         pointMatOffsets[0][p + 1] = 0;
7274         continue;
7275       }
7276       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7277       if (bDof) {
7278         PetscInt bOff, q, allDof = 0;
7279 
7280         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7281         for (q = 0; q < bDof; q++) {
7282           PetscInt a = anchors[bOff + q], aDof;
7283 
7284           PetscCall(PetscSectionGetDof(section, a, &aDof));
7285           allDof += aDof;
7286         }
7287         newPointOffsets[0][p + 1] = allDof;
7288         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7289       } else {
7290         newPointOffsets[0][p + 1] = bSecDof;
7291         pointMatOffsets[0][p + 1] = 0;
7292       }
7293     }
7294     newPointOffsets[0][0] = 0;
7295     pointMatOffsets[0][0] = 0;
7296     for (p = 0; p < numPoints; p++) {
7297       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7298       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7299     }
7300     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7301   }
7302 
7303   /* output arrays */
7304   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7305 
7306   /* get the point-to-point matrices; construct newPoints */
7307   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7308   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7309   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7310   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7311   if (numFields) {
7312     for (p = 0, newP = 0; p < numPoints; p++) {
7313       PetscInt b    = points[2 * p];
7314       PetscInt o    = points[2 * p + 1];
7315       PetscInt bDof = 0, bSecDof;
7316 
7317       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7318       if (!bSecDof) continue;
7319       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7320       if (bDof) {
7321         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7322 
7323         fStart[0] = 0;
7324         fEnd[0]   = 0;
7325         for (f = 0; f < numFields; f++) {
7326           PetscInt fDof;
7327 
7328           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7329           fStart[f + 1] = fStart[f] + fDof;
7330           fEnd[f + 1]   = fStart[f + 1];
7331         }
7332         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7333         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7334 
7335         fAnchorStart[0] = 0;
7336         fAnchorEnd[0]   = 0;
7337         for (f = 0; f < numFields; f++) {
7338           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7339 
7340           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7341           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7342         }
7343         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7344         for (q = 0; q < bDof; q++) {
7345           PetscInt a = anchors[bOff + q], aOff;
7346 
7347           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7348           newPoints[2 * (newP + q)]     = a;
7349           newPoints[2 * (newP + q) + 1] = 0;
7350           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7351           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7352         }
7353         newP += bDof;
7354 
7355         if (outValues) {
7356           /* get the point-to-point submatrix */
7357           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]));
7358         }
7359       } else {
7360         newPoints[2 * newP]     = b;
7361         newPoints[2 * newP + 1] = o;
7362         newP++;
7363       }
7364     }
7365   } else {
7366     for (p = 0; p < numPoints; p++) {
7367       PetscInt b    = points[2 * p];
7368       PetscInt o    = points[2 * p + 1];
7369       PetscInt bDof = 0, bSecDof;
7370 
7371       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7372       if (!bSecDof) continue;
7373       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7374       if (bDof) {
7375         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7376 
7377         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7378         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7379 
7380         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7381         for (q = 0; q < bDof; q++) {
7382           PetscInt a = anchors[bOff + q], aOff;
7383 
7384           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7385 
7386           newPoints[2 * (newP + q)]     = a;
7387           newPoints[2 * (newP + q) + 1] = 0;
7388           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7389           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7390         }
7391         newP += bDof;
7392 
7393         /* get the point-to-point submatrix */
7394         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7395       } else {
7396         newPoints[2 * newP]     = b;
7397         newPoints[2 * newP + 1] = o;
7398         newP++;
7399       }
7400     }
7401   }
7402 
7403   if (outValues) {
7404     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7405     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7406     /* multiply constraints on the right */
7407     if (numFields) {
7408       for (f = 0; f < numFields; f++) {
7409         PetscInt oldOff = offsets[f];
7410 
7411         for (p = 0; p < numPoints; p++) {
7412           PetscInt cStart = newPointOffsets[f][p];
7413           PetscInt b      = points[2 * p];
7414           PetscInt c, r, k;
7415           PetscInt dof;
7416 
7417           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7418           if (!dof) continue;
7419           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7420             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7421             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7422 
7423             for (r = 0; r < numIndices; r++) {
7424               for (c = 0; c < nCols; c++) {
7425                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7426               }
7427             }
7428           } else {
7429             /* copy this column as is */
7430             for (r = 0; r < numIndices; r++) {
7431               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7432             }
7433           }
7434           oldOff += dof;
7435         }
7436       }
7437     } else {
7438       PetscInt oldOff = 0;
7439       for (p = 0; p < numPoints; p++) {
7440         PetscInt cStart = newPointOffsets[0][p];
7441         PetscInt b      = points[2 * p];
7442         PetscInt c, r, k;
7443         PetscInt dof;
7444 
7445         PetscCall(PetscSectionGetDof(section, b, &dof));
7446         if (!dof) continue;
7447         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7448           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7449           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7450 
7451           for (r = 0; r < numIndices; r++) {
7452             for (c = 0; c < nCols; c++) {
7453               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7454             }
7455           }
7456         } else {
7457           /* copy this column as is */
7458           for (r = 0; r < numIndices; r++) {
7459             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7460           }
7461         }
7462         oldOff += dof;
7463       }
7464     }
7465 
7466     if (multiplyLeft) {
7467       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7468       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7469       /* multiply constraints transpose on the left */
7470       if (numFields) {
7471         for (f = 0; f < numFields; f++) {
7472           PetscInt oldOff = offsets[f];
7473 
7474           for (p = 0; p < numPoints; p++) {
7475             PetscInt rStart = newPointOffsets[f][p];
7476             PetscInt b      = points[2 * p];
7477             PetscInt c, r, k;
7478             PetscInt dof;
7479 
7480             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7481             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7482               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7483               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7484 
7485               for (r = 0; r < nRows; r++) {
7486                 for (c = 0; c < newNumIndices; c++) {
7487                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7488                 }
7489               }
7490             } else {
7491               /* copy this row as is */
7492               for (r = 0; r < dof; r++) {
7493                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7494               }
7495             }
7496             oldOff += dof;
7497           }
7498         }
7499       } else {
7500         PetscInt oldOff = 0;
7501 
7502         for (p = 0; p < numPoints; p++) {
7503           PetscInt rStart = newPointOffsets[0][p];
7504           PetscInt b      = points[2 * p];
7505           PetscInt c, r, k;
7506           PetscInt dof;
7507 
7508           PetscCall(PetscSectionGetDof(section, b, &dof));
7509           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7510             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7511             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7512 
7513             for (r = 0; r < nRows; r++) {
7514               for (c = 0; c < newNumIndices; c++) {
7515                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7516               }
7517             }
7518           } else {
7519             /* copy this row as is */
7520             for (r = 0; r < dof; r++) {
7521               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7522             }
7523           }
7524           oldOff += dof;
7525         }
7526       }
7527 
7528       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7529     } else {
7530       newValues = tmpValues;
7531     }
7532   }
7533 
7534   /* clean up */
7535   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7536   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7537 
7538   if (numFields) {
7539     for (f = 0; f < numFields; f++) {
7540       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7541       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7542       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7543     }
7544   } else {
7545     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7546     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7547     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7548   }
7549   PetscCall(ISRestoreIndices(aIS, &anchors));
7550 
7551   /* output */
7552   if (outPoints) {
7553     *outPoints = newPoints;
7554   } else {
7555     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7556   }
7557   if (outValues) *outValues = newValues;
7558   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7559   PetscFunctionReturn(PETSC_SUCCESS);
7560 }
7561 
7562 /*@C
7563   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7564 
7565   Not collective
7566 
7567   Input Parameters:
7568 + dm         - The `DM`
7569 . section    - The `PetscSection` describing the points (a local section)
7570 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7571 . point      - The point defining the closure
7572 - useClPerm  - Use the closure point permutation if available
7573 
7574   Output Parameters:
7575 + numIndices - The number of dof indices in the closure of point with the input sections
7576 . indices    - The dof indices
7577 . outOffsets - Array to write the field offsets into, or NULL
7578 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7579 
7580   Level: advanced
7581 
7582   Notes:
7583   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7584 
7585   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7586   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7587   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7588   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7589   indices (with the above semantics) are implied.
7590 
7591 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7592           `PetscSection`, `DMGetGlobalSection()`
7593 @*/
7594 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7595 {
7596   /* Closure ordering */
7597   PetscSection    clSection;
7598   IS              clPoints;
7599   const PetscInt *clp;
7600   PetscInt       *points;
7601   const PetscInt *clperm = NULL;
7602   /* Dof permutation and sign flips */
7603   const PetscInt    **perms[32] = {NULL};
7604   const PetscScalar **flips[32] = {NULL};
7605   PetscScalar        *valCopy   = NULL;
7606   /* Hanging node constraints */
7607   PetscInt    *pointsC = NULL;
7608   PetscScalar *valuesC = NULL;
7609   PetscInt     NclC, NiC;
7610 
7611   PetscInt *idx;
7612   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7613   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7614 
7615   PetscFunctionBeginHot;
7616   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7617   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7618   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7619   if (numIndices) PetscValidIntPointer(numIndices, 6);
7620   if (indices) PetscValidPointer(indices, 7);
7621   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7622   if (values) PetscValidPointer(values, 9);
7623   PetscCall(PetscSectionGetNumFields(section, &Nf));
7624   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7625   PetscCall(PetscArrayzero(offsets, 32));
7626   /* 1) Get points in closure */
7627   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7628   if (useClPerm) {
7629     PetscInt depth, clsize;
7630     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7631     for (clsize = 0, p = 0; p < Ncl; p++) {
7632       PetscInt dof;
7633       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7634       clsize += dof;
7635     }
7636     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7637   }
7638   /* 2) Get number of indices on these points and field offsets from section */
7639   for (p = 0; p < Ncl * 2; p += 2) {
7640     PetscInt dof, fdof;
7641 
7642     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7643     for (f = 0; f < Nf; ++f) {
7644       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7645       offsets[f + 1] += fdof;
7646     }
7647     Ni += dof;
7648   }
7649   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7650   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7651   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7652   for (f = 0; f < PetscMax(1, Nf); ++f) {
7653     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7654     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7655     /* may need to apply sign changes to the element matrix */
7656     if (values && flips[f]) {
7657       PetscInt foffset = offsets[f];
7658 
7659       for (p = 0; p < Ncl; ++p) {
7660         PetscInt           pnt  = points[2 * p], fdof;
7661         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7662 
7663         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7664         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7665         if (flip) {
7666           PetscInt i, j, k;
7667 
7668           if (!valCopy) {
7669             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7670             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7671             *values = valCopy;
7672           }
7673           for (i = 0; i < fdof; ++i) {
7674             PetscScalar fval = flip[i];
7675 
7676             for (k = 0; k < Ni; ++k) {
7677               valCopy[Ni * (foffset + i) + k] *= fval;
7678               valCopy[Ni * k + (foffset + i)] *= fval;
7679             }
7680           }
7681         }
7682         foffset += fdof;
7683       }
7684     }
7685   }
7686   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7687   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7688   if (NclC) {
7689     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7690     for (f = 0; f < PetscMax(1, Nf); ++f) {
7691       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7692       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7693     }
7694     for (f = 0; f < PetscMax(1, Nf); ++f) {
7695       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7696       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7697     }
7698     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7699     Ncl    = NclC;
7700     Ni     = NiC;
7701     points = pointsC;
7702     if (values) *values = valuesC;
7703   }
7704   /* 5) Calculate indices */
7705   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7706   if (Nf) {
7707     PetscInt  idxOff;
7708     PetscBool useFieldOffsets;
7709 
7710     if (outOffsets) {
7711       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7712     }
7713     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7714     if (useFieldOffsets) {
7715       for (p = 0; p < Ncl; ++p) {
7716         const PetscInt pnt = points[p * 2];
7717 
7718         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7719       }
7720     } else {
7721       for (p = 0; p < Ncl; ++p) {
7722         const PetscInt pnt = points[p * 2];
7723 
7724         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7725         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7726          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7727          * global section. */
7728         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7729       }
7730     }
7731   } else {
7732     PetscInt off = 0, idxOff;
7733 
7734     for (p = 0; p < Ncl; ++p) {
7735       const PetscInt  pnt  = points[p * 2];
7736       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7737 
7738       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7739       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7740        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7741       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7742     }
7743   }
7744   /* 6) Cleanup */
7745   for (f = 0; f < PetscMax(1, Nf); ++f) {
7746     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7747     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7748   }
7749   if (NclC) {
7750     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7751   } else {
7752     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7753   }
7754 
7755   if (numIndices) *numIndices = Ni;
7756   if (indices) *indices = idx;
7757   PetscFunctionReturn(PETSC_SUCCESS);
7758 }
7759 
7760 /*@C
7761   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7762 
7763   Not collective
7764 
7765   Input Parameters:
7766 + dm         - The `DM`
7767 . section    - The `PetscSection` describing the points (a local section)
7768 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7769 . point      - The point defining the closure
7770 - useClPerm  - Use the closure point permutation if available
7771 
7772   Output Parameters:
7773 + numIndices - The number of dof indices in the closure of point with the input sections
7774 . indices    - The dof indices
7775 . outOffsets - Array to write the field offsets into, or NULL
7776 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7777 
7778   Level: advanced
7779 
7780   Notes:
7781   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7782 
7783   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7784   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7785   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7786   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7787   indices (with the above semantics) are implied.
7788 
7789 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7790 @*/
7791 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7792 {
7793   PetscFunctionBegin;
7794   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7795   PetscValidPointer(indices, 7);
7796   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7797   PetscFunctionReturn(PETSC_SUCCESS);
7798 }
7799 
7800 /*@C
7801   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7802 
7803   Not collective
7804 
7805   Input Parameters:
7806 + dm - The `DM`
7807 . section - The section describing the layout in v, or NULL to use the default section
7808 . globalSection - The section describing the layout in v, or NULL to use the default global section
7809 . A - The matrix
7810 . point - The point in the `DM`
7811 . values - The array of values
7812 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7813 
7814   Level: intermediate
7815 
7816 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7817 @*/
7818 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7819 {
7820   DM_Plex           *mesh = (DM_Plex *)dm->data;
7821   PetscInt          *indices;
7822   PetscInt           numIndices;
7823   const PetscScalar *valuesOrig = values;
7824   PetscErrorCode     ierr;
7825 
7826   PetscFunctionBegin;
7827   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7828   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7829   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7830   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7831   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7832   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7833 
7834   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7835 
7836   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7837   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7838   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7839   if (ierr) {
7840     PetscMPIInt rank;
7841 
7842     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7843     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7844     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7845     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7846     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7847     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7848   }
7849   if (mesh->printFEM > 1) {
7850     PetscInt i;
7851     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7852     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7853     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7854   }
7855 
7856   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7857   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7858   PetscFunctionReturn(PETSC_SUCCESS);
7859 }
7860 
7861 /*@C
7862   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7863 
7864   Not collective
7865 
7866   Input Parameters:
7867 + dmRow - The `DM` for the row fields
7868 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7869 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7870 . dmCol - The `DM` for the column fields
7871 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7872 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7873 . A - The matrix
7874 . point - The point in the `DM`
7875 . values - The array of values
7876 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7877 
7878   Level: intermediate
7879 
7880 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7881 @*/
7882 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7883 {
7884   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7885   PetscInt          *indicesRow, *indicesCol;
7886   PetscInt           numIndicesRow, numIndicesCol;
7887   const PetscScalar *valuesOrig = values;
7888   PetscErrorCode     ierr;
7889 
7890   PetscFunctionBegin;
7891   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7892   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7893   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7894   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7895   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7896   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7897   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7898   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7899   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7900   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7901   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7902 
7903   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7904   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7905 
7906   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7907   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7908   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7909   if (ierr) {
7910     PetscMPIInt rank;
7911 
7912     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7913     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7914     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7915     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7916     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7917     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7918   }
7919 
7920   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7921   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7922   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7923   PetscFunctionReturn(PETSC_SUCCESS);
7924 }
7925 
7926 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7927 {
7928   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7929   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7930   PetscInt       *cpoints = NULL;
7931   PetscInt       *findices, *cindices;
7932   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7933   PetscInt        foffsets[32], coffsets[32];
7934   DMPolytopeType  ct;
7935   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7936   PetscErrorCode  ierr;
7937 
7938   PetscFunctionBegin;
7939   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7940   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7941   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7942   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7943   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7944   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7945   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7946   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7947   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7948   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7949   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7950   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7951   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7952   PetscCall(PetscArrayzero(foffsets, 32));
7953   PetscCall(PetscArrayzero(coffsets, 32));
7954   /* Column indices */
7955   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7956   maxFPoints = numCPoints;
7957   /* Compress out points not in the section */
7958   /*   TODO: Squeeze out points with 0 dof as well */
7959   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7960   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7961     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7962       cpoints[q * 2]     = cpoints[p];
7963       cpoints[q * 2 + 1] = cpoints[p + 1];
7964       ++q;
7965     }
7966   }
7967   numCPoints = q;
7968   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7969     PetscInt fdof;
7970 
7971     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7972     if (!dof) continue;
7973     for (f = 0; f < numFields; ++f) {
7974       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7975       coffsets[f + 1] += fdof;
7976     }
7977     numCIndices += dof;
7978   }
7979   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7980   /* Row indices */
7981   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7982   {
7983     DMPlexTransform tr;
7984     DMPolytopeType *rct;
7985     PetscInt       *rsize, *rcone, *rornt, Nt;
7986 
7987     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7988     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7989     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7990     numSubcells = rsize[Nt - 1];
7991     PetscCall(DMPlexTransformDestroy(&tr));
7992   }
7993   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7994   for (r = 0, q = 0; r < numSubcells; ++r) {
7995     /* TODO Map from coarse to fine cells */
7996     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7997     /* Compress out points not in the section */
7998     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7999     for (p = 0; p < numFPoints * 2; p += 2) {
8000       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8001         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8002         if (!dof) continue;
8003         for (s = 0; s < q; ++s)
8004           if (fpoints[p] == ftotpoints[s * 2]) break;
8005         if (s < q) continue;
8006         ftotpoints[q * 2]     = fpoints[p];
8007         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8008         ++q;
8009       }
8010     }
8011     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8012   }
8013   numFPoints = q;
8014   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8015     PetscInt fdof;
8016 
8017     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8018     if (!dof) continue;
8019     for (f = 0; f < numFields; ++f) {
8020       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8021       foffsets[f + 1] += fdof;
8022     }
8023     numFIndices += dof;
8024   }
8025   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8026 
8027   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8028   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8029   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8030   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8031   if (numFields) {
8032     const PetscInt **permsF[32] = {NULL};
8033     const PetscInt **permsC[32] = {NULL};
8034 
8035     for (f = 0; f < numFields; f++) {
8036       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8037       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8038     }
8039     for (p = 0; p < numFPoints; p++) {
8040       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8041       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8042     }
8043     for (p = 0; p < numCPoints; p++) {
8044       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8045       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8046     }
8047     for (f = 0; f < numFields; f++) {
8048       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8049       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8050     }
8051   } else {
8052     const PetscInt **permsF = NULL;
8053     const PetscInt **permsC = NULL;
8054 
8055     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8056     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8057     for (p = 0, off = 0; p < numFPoints; p++) {
8058       const PetscInt *perm = permsF ? permsF[p] : NULL;
8059 
8060       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8061       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8062     }
8063     for (p = 0, off = 0; p < numCPoints; p++) {
8064       const PetscInt *perm = permsC ? permsC[p] : NULL;
8065 
8066       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8067       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8068     }
8069     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8070     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8071   }
8072   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8073   /* TODO: flips */
8074   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8075   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8076   if (ierr) {
8077     PetscMPIInt rank;
8078 
8079     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8080     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8081     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8082     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8083     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8084   }
8085   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8086   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8087   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8088   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8089   PetscFunctionReturn(PETSC_SUCCESS);
8090 }
8091 
8092 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8093 {
8094   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8095   PetscInt       *cpoints = NULL;
8096   PetscInt        foffsets[32], coffsets[32];
8097   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8098   DMPolytopeType  ct;
8099   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8100 
8101   PetscFunctionBegin;
8102   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8103   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8104   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8105   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8106   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8107   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8108   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8109   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8110   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8111   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8112   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8113   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8114   PetscCall(PetscArrayzero(foffsets, 32));
8115   PetscCall(PetscArrayzero(coffsets, 32));
8116   /* Column indices */
8117   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8118   maxFPoints = numCPoints;
8119   /* Compress out points not in the section */
8120   /*   TODO: Squeeze out points with 0 dof as well */
8121   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8122   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8123     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8124       cpoints[q * 2]     = cpoints[p];
8125       cpoints[q * 2 + 1] = cpoints[p + 1];
8126       ++q;
8127     }
8128   }
8129   numCPoints = q;
8130   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8131     PetscInt fdof;
8132 
8133     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8134     if (!dof) continue;
8135     for (f = 0; f < numFields; ++f) {
8136       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8137       coffsets[f + 1] += fdof;
8138     }
8139     numCIndices += dof;
8140   }
8141   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8142   /* Row indices */
8143   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8144   {
8145     DMPlexTransform tr;
8146     DMPolytopeType *rct;
8147     PetscInt       *rsize, *rcone, *rornt, Nt;
8148 
8149     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8150     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8151     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8152     numSubcells = rsize[Nt - 1];
8153     PetscCall(DMPlexTransformDestroy(&tr));
8154   }
8155   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8156   for (r = 0, q = 0; r < numSubcells; ++r) {
8157     /* TODO Map from coarse to fine cells */
8158     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8159     /* Compress out points not in the section */
8160     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8161     for (p = 0; p < numFPoints * 2; p += 2) {
8162       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8163         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8164         if (!dof) continue;
8165         for (s = 0; s < q; ++s)
8166           if (fpoints[p] == ftotpoints[s * 2]) break;
8167         if (s < q) continue;
8168         ftotpoints[q * 2]     = fpoints[p];
8169         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8170         ++q;
8171       }
8172     }
8173     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8174   }
8175   numFPoints = q;
8176   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8177     PetscInt fdof;
8178 
8179     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8180     if (!dof) continue;
8181     for (f = 0; f < numFields; ++f) {
8182       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8183       foffsets[f + 1] += fdof;
8184     }
8185     numFIndices += dof;
8186   }
8187   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8188 
8189   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8190   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8191   if (numFields) {
8192     const PetscInt **permsF[32] = {NULL};
8193     const PetscInt **permsC[32] = {NULL};
8194 
8195     for (f = 0; f < numFields; f++) {
8196       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8197       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8198     }
8199     for (p = 0; p < numFPoints; p++) {
8200       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8201       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8202     }
8203     for (p = 0; p < numCPoints; p++) {
8204       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8205       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8206     }
8207     for (f = 0; f < numFields; f++) {
8208       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8209       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8210     }
8211   } else {
8212     const PetscInt **permsF = NULL;
8213     const PetscInt **permsC = NULL;
8214 
8215     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8216     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8217     for (p = 0, off = 0; p < numFPoints; p++) {
8218       const PetscInt *perm = permsF ? permsF[p] : NULL;
8219 
8220       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8221       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8222     }
8223     for (p = 0, off = 0; p < numCPoints; p++) {
8224       const PetscInt *perm = permsC ? permsC[p] : NULL;
8225 
8226       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8227       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8228     }
8229     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8230     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8231   }
8232   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8233   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8234   PetscFunctionReturn(PETSC_SUCCESS);
8235 }
8236 
8237 /*@C
8238   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8239 
8240   Input Parameter:
8241 . dm   - The `DMPLEX` object
8242 
8243   Output Parameter:
8244 . cellHeight - The height of a cell
8245 
8246   Level: developer
8247 
8248 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8249 @*/
8250 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8251 {
8252   DM_Plex *mesh = (DM_Plex *)dm->data;
8253 
8254   PetscFunctionBegin;
8255   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8256   PetscValidIntPointer(cellHeight, 2);
8257   *cellHeight = mesh->vtkCellHeight;
8258   PetscFunctionReturn(PETSC_SUCCESS);
8259 }
8260 
8261 /*@C
8262   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8263 
8264   Input Parameters:
8265 + dm   - The `DMPLEX` object
8266 - cellHeight - The height of a cell
8267 
8268   Level: developer
8269 
8270 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8271 @*/
8272 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8273 {
8274   DM_Plex *mesh = (DM_Plex *)dm->data;
8275 
8276   PetscFunctionBegin;
8277   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8278   mesh->vtkCellHeight = cellHeight;
8279   PetscFunctionReturn(PETSC_SUCCESS);
8280 }
8281 
8282 /*@
8283   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8284 
8285   Input Parameter:
8286 . dm - The `DMPLEX` object
8287 
8288   Output Parameters:
8289 + gcStart - The first ghost cell, or NULL
8290 - gcEnd   - The upper bound on ghost cells, or NULL
8291 
8292   Level: advanced
8293 
8294 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8295 @*/
8296 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8297 {
8298   DMLabel ctLabel;
8299 
8300   PetscFunctionBegin;
8301   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8302   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8303   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8304   // Reset label for fast lookup
8305   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8306   PetscFunctionReturn(PETSC_SUCCESS);
8307 }
8308 
8309 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8310 {
8311   PetscSection section, globalSection;
8312   PetscInt    *numbers, p;
8313 
8314   PetscFunctionBegin;
8315   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8316   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8317   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8318   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8319   PetscCall(PetscSectionSetUp(section));
8320   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8321   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8322   for (p = pStart; p < pEnd; ++p) {
8323     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8324     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8325     else numbers[p - pStart] += shift;
8326   }
8327   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8328   if (globalSize) {
8329     PetscLayout layout;
8330     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8331     PetscCall(PetscLayoutGetSize(layout, globalSize));
8332     PetscCall(PetscLayoutDestroy(&layout));
8333   }
8334   PetscCall(PetscSectionDestroy(&section));
8335   PetscCall(PetscSectionDestroy(&globalSection));
8336   PetscFunctionReturn(PETSC_SUCCESS);
8337 }
8338 
8339 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8340 {
8341   PetscInt cellHeight, cStart, cEnd;
8342 
8343   PetscFunctionBegin;
8344   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8345   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8346   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8347   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8348   PetscFunctionReturn(PETSC_SUCCESS);
8349 }
8350 
8351 /*@
8352   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8353 
8354   Input Parameter:
8355 . dm   - The `DMPLEX` object
8356 
8357   Output Parameter:
8358 . globalCellNumbers - Global cell numbers for all cells on this process
8359 
8360   Level: developer
8361 
8362 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8363 @*/
8364 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8365 {
8366   DM_Plex *mesh = (DM_Plex *)dm->data;
8367 
8368   PetscFunctionBegin;
8369   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8370   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8371   *globalCellNumbers = mesh->globalCellNumbers;
8372   PetscFunctionReturn(PETSC_SUCCESS);
8373 }
8374 
8375 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8376 {
8377   PetscInt vStart, vEnd;
8378 
8379   PetscFunctionBegin;
8380   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8381   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8382   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8383   PetscFunctionReturn(PETSC_SUCCESS);
8384 }
8385 
8386 /*@
8387   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8388 
8389   Input Parameter:
8390 . dm   - The `DMPLEX` object
8391 
8392   Output Parameter:
8393 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8394 
8395   Level: developer
8396 
8397 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8398 @*/
8399 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8400 {
8401   DM_Plex *mesh = (DM_Plex *)dm->data;
8402 
8403   PetscFunctionBegin;
8404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8405   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8406   *globalVertexNumbers = mesh->globalVertexNumbers;
8407   PetscFunctionReturn(PETSC_SUCCESS);
8408 }
8409 
8410 /*@
8411   DMPlexCreatePointNumbering - Create a global numbering for all points.
8412 
8413   Collective on dm
8414 
8415   Input Parameter:
8416 . dm   - The `DMPLEX` object
8417 
8418   Output Parameter:
8419 . globalPointNumbers - Global numbers for all points on this process
8420 
8421   Level: developer
8422 
8423   Notes:
8424   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8425   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8426   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8427   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8428 
8429   The partitioned mesh is
8430 ```
8431  (2)--0--(3)--1--(4)    (1)--0--(2)
8432 ```
8433   and its global numbering is
8434 ```
8435   (3)--0--(4)--1--(5)--2--(6)
8436 ```
8437   Then the global numbering is provided as
8438 ```
8439 [0] Number of indices in set 5
8440 [0] 0 0
8441 [0] 1 1
8442 [0] 2 3
8443 [0] 3 4
8444 [0] 4 -6
8445 [1] Number of indices in set 3
8446 [1] 0 2
8447 [1] 1 5
8448 [1] 2 6
8449 ```
8450 
8451 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8452 @*/
8453 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8454 {
8455   IS        nums[4];
8456   PetscInt  depths[4], gdepths[4], starts[4];
8457   PetscInt  depth, d, shift = 0;
8458   PetscBool empty = PETSC_FALSE;
8459 
8460   PetscFunctionBegin;
8461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8462   PetscCall(DMPlexGetDepth(dm, &depth));
8463   // For unstratified meshes use dim instead of depth
8464   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8465   // If any stratum is empty, we must mark all empty
8466   for (d = 0; d <= depth; ++d) {
8467     PetscInt end;
8468 
8469     depths[d] = depth - d;
8470     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8471     if (!(starts[d] - end)) empty = PETSC_TRUE;
8472   }
8473   if (empty)
8474     for (d = 0; d <= depth; ++d) {
8475       depths[d] = -1;
8476       starts[d] = -1;
8477     }
8478   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8479   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8480   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]);
8481   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8482   for (d = 0; d <= depth; ++d) {
8483     PetscInt pStart, pEnd, gsize;
8484 
8485     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8486     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8487     shift += gsize;
8488   }
8489   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8490   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8491   PetscFunctionReturn(PETSC_SUCCESS);
8492 }
8493 
8494 /*@
8495   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8496 
8497   Input Parameter:
8498 . dm - The `DMPLEX` object
8499 
8500   Output Parameter:
8501 . ranks - The rank field
8502 
8503   Options Database Key:
8504 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8505 
8506   Level: intermediate
8507 
8508 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8509 @*/
8510 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8511 {
8512   DM             rdm;
8513   PetscFE        fe;
8514   PetscScalar   *r;
8515   PetscMPIInt    rank;
8516   DMPolytopeType ct;
8517   PetscInt       dim, cStart, cEnd, c;
8518   PetscBool      simplex;
8519 
8520   PetscFunctionBeginUser;
8521   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8522   PetscValidPointer(ranks, 2);
8523   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8524   PetscCall(DMClone(dm, &rdm));
8525   PetscCall(DMGetDimension(rdm, &dim));
8526   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8527   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8528   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8529   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8530   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8531   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8532   PetscCall(PetscFEDestroy(&fe));
8533   PetscCall(DMCreateDS(rdm));
8534   PetscCall(DMCreateGlobalVector(rdm, ranks));
8535   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8536   PetscCall(VecGetArray(*ranks, &r));
8537   for (c = cStart; c < cEnd; ++c) {
8538     PetscScalar *lr;
8539 
8540     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8541     if (lr) *lr = rank;
8542   }
8543   PetscCall(VecRestoreArray(*ranks, &r));
8544   PetscCall(DMDestroy(&rdm));
8545   PetscFunctionReturn(PETSC_SUCCESS);
8546 }
8547 
8548 /*@
8549   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8550 
8551   Input Parameters:
8552 + dm    - The DMPlex
8553 - label - The DMLabel
8554 
8555   Output Parameter:
8556 . val - The label value field
8557 
8558   Options Database Keys:
8559 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8560 
8561   Level: intermediate
8562 
8563 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8564 @*/
8565 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8566 {
8567   DM           rdm;
8568   PetscFE      fe;
8569   PetscScalar *v;
8570   PetscInt     dim, cStart, cEnd, c;
8571 
8572   PetscFunctionBeginUser;
8573   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8574   PetscValidPointer(label, 2);
8575   PetscValidPointer(val, 3);
8576   PetscCall(DMClone(dm, &rdm));
8577   PetscCall(DMGetDimension(rdm, &dim));
8578   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8579   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8580   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8581   PetscCall(PetscFEDestroy(&fe));
8582   PetscCall(DMCreateDS(rdm));
8583   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8584   PetscCall(DMCreateGlobalVector(rdm, val));
8585   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8586   PetscCall(VecGetArray(*val, &v));
8587   for (c = cStart; c < cEnd; ++c) {
8588     PetscScalar *lv;
8589     PetscInt     cval;
8590 
8591     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8592     PetscCall(DMLabelGetValue(label, c, &cval));
8593     *lv = cval;
8594   }
8595   PetscCall(VecRestoreArray(*val, &v));
8596   PetscCall(DMDestroy(&rdm));
8597   PetscFunctionReturn(PETSC_SUCCESS);
8598 }
8599 
8600 /*@
8601   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8602 
8603   Input Parameter:
8604 . dm - The `DMPLEX` object
8605 
8606   Level: developer
8607 
8608   Notes:
8609   This is a useful diagnostic when creating meshes programmatically.
8610 
8611   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8612 
8613 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8614 @*/
8615 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8616 {
8617   PetscSection    coneSection, supportSection;
8618   const PetscInt *cone, *support;
8619   PetscInt        coneSize, c, supportSize, s;
8620   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8621   PetscBool       storagecheck = PETSC_TRUE;
8622 
8623   PetscFunctionBegin;
8624   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8625   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8626   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8627   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8628   /* Check that point p is found in the support of its cone points, and vice versa */
8629   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8630   for (p = pStart; p < pEnd; ++p) {
8631     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8632     PetscCall(DMPlexGetCone(dm, p, &cone));
8633     for (c = 0; c < coneSize; ++c) {
8634       PetscBool dup = PETSC_FALSE;
8635       PetscInt  d;
8636       for (d = c - 1; d >= 0; --d) {
8637         if (cone[c] == cone[d]) {
8638           dup = PETSC_TRUE;
8639           break;
8640         }
8641       }
8642       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8643       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8644       for (s = 0; s < supportSize; ++s) {
8645         if (support[s] == p) break;
8646       }
8647       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8648         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8649         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8650         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8651         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8652         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8653         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8654         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]);
8655         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8656       }
8657     }
8658     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8659     if (p != pp) {
8660       storagecheck = PETSC_FALSE;
8661       continue;
8662     }
8663     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8664     PetscCall(DMPlexGetSupport(dm, p, &support));
8665     for (s = 0; s < supportSize; ++s) {
8666       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8667       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8668       for (c = 0; c < coneSize; ++c) {
8669         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8670         if (cone[c] != pp) {
8671           c = 0;
8672           break;
8673         }
8674         if (cone[c] == p) break;
8675       }
8676       if (c >= coneSize) {
8677         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8678         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8679         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8680         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8681         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8682         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8683         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8684       }
8685     }
8686   }
8687   if (storagecheck) {
8688     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8689     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8690     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8691   }
8692   PetscFunctionReturn(PETSC_SUCCESS);
8693 }
8694 
8695 /*
8696   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.
8697 */
8698 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8699 {
8700   DMPolytopeType  cct;
8701   PetscInt        ptpoints[4];
8702   const PetscInt *cone, *ccone, *ptcone;
8703   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8704 
8705   PetscFunctionBegin;
8706   *unsplit = 0;
8707   switch (ct) {
8708   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8709     ptpoints[npt++] = c;
8710     break;
8711   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8712     PetscCall(DMPlexGetCone(dm, c, &cone));
8713     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8714     for (cp = 0; cp < coneSize; ++cp) {
8715       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8716       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8717     }
8718     break;
8719   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8720   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8721     PetscCall(DMPlexGetCone(dm, c, &cone));
8722     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8723     for (cp = 0; cp < coneSize; ++cp) {
8724       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8725       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8726       for (ccp = 0; ccp < cconeSize; ++ccp) {
8727         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8728         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8729           PetscInt p;
8730           for (p = 0; p < npt; ++p)
8731             if (ptpoints[p] == ccone[ccp]) break;
8732           if (p == npt) ptpoints[npt++] = ccone[ccp];
8733         }
8734       }
8735     }
8736     break;
8737   default:
8738     break;
8739   }
8740   for (pt = 0; pt < npt; ++pt) {
8741     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8742     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8743   }
8744   PetscFunctionReturn(PETSC_SUCCESS);
8745 }
8746 
8747 /*@
8748   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8749 
8750   Input Parameters:
8751 + dm - The `DMPLEX` object
8752 - cellHeight - Normally 0
8753 
8754   Level: developer
8755 
8756   Notes:
8757   This is a useful diagnostic when creating meshes programmatically.
8758   Currently applicable only to homogeneous simplex or tensor meshes.
8759 
8760   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8761 
8762 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8763 @*/
8764 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8765 {
8766   DMPlexInterpolatedFlag interp;
8767   DMPolytopeType         ct;
8768   PetscInt               vStart, vEnd, cStart, cEnd, c;
8769 
8770   PetscFunctionBegin;
8771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8772   PetscCall(DMPlexIsInterpolated(dm, &interp));
8773   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8774   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8775   for (c = cStart; c < cEnd; ++c) {
8776     PetscInt *closure = NULL;
8777     PetscInt  coneSize, closureSize, cl, Nv = 0;
8778 
8779     PetscCall(DMPlexGetCellType(dm, c, &ct));
8780     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8781     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8782     if (interp == DMPLEX_INTERPOLATED_FULL) {
8783       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8784       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));
8785     }
8786     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8787     for (cl = 0; cl < closureSize * 2; cl += 2) {
8788       const PetscInt p = closure[cl];
8789       if ((p >= vStart) && (p < vEnd)) ++Nv;
8790     }
8791     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8792     /* Special Case: Tensor faces with identified vertices */
8793     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8794       PetscInt unsplit;
8795 
8796       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8797       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8798     }
8799     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));
8800   }
8801   PetscFunctionReturn(PETSC_SUCCESS);
8802 }
8803 
8804 /*@
8805   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8806 
8807   Collective on dm
8808 
8809   Input Parameters:
8810 + dm - The `DMPLEX` object
8811 - cellHeight - Normally 0
8812 
8813   Level: developer
8814 
8815   Notes:
8816   This is a useful diagnostic when creating meshes programmatically.
8817   This routine is only relevant for meshes that are fully interpolated across all ranks.
8818   It will error out if a partially interpolated mesh is given on some rank.
8819   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8820 
8821   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8822 
8823 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8824 @*/
8825 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8826 {
8827   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8828   DMPlexInterpolatedFlag interpEnum;
8829 
8830   PetscFunctionBegin;
8831   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8832   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8833   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8834   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8835     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8836     PetscFunctionReturn(PETSC_SUCCESS);
8837   }
8838 
8839   PetscCall(DMGetDimension(dm, &dim));
8840   PetscCall(DMPlexGetDepth(dm, &depth));
8841   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8842   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8843     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8844     for (c = cStart; c < cEnd; ++c) {
8845       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8846       const DMPolytopeType *faceTypes;
8847       DMPolytopeType        ct;
8848       PetscInt              numFaces, coneSize, f;
8849       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8850 
8851       PetscCall(DMPlexGetCellType(dm, c, &ct));
8852       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8853       if (unsplit) continue;
8854       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8855       PetscCall(DMPlexGetCone(dm, c, &cone));
8856       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8857       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8858       for (cl = 0; cl < closureSize * 2; cl += 2) {
8859         const PetscInt p = closure[cl];
8860         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8861       }
8862       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8863       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);
8864       for (f = 0; f < numFaces; ++f) {
8865         DMPolytopeType fct;
8866         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8867 
8868         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8869         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8870         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8871           const PetscInt p = fclosure[cl];
8872           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8873         }
8874         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]);
8875         for (v = 0; v < fnumCorners; ++v) {
8876           if (fclosure[v] != faces[fOff + v]) {
8877             PetscInt v1;
8878 
8879             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8880             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8881             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8882             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8883             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8884             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]);
8885           }
8886         }
8887         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8888         fOff += faceSizes[f];
8889       }
8890       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8891       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8892     }
8893   }
8894   PetscFunctionReturn(PETSC_SUCCESS);
8895 }
8896 
8897 /*@
8898   DMPlexCheckGeometry - Check the geometry of mesh cells
8899 
8900   Input Parameter:
8901 . dm - The `DMPLEX` object
8902 
8903   Level: developer
8904 
8905   Notes:
8906   This is a useful diagnostic when creating meshes programmatically.
8907 
8908   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8909 
8910 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8911 @*/
8912 PetscErrorCode DMPlexCheckGeometry(DM dm)
8913 {
8914   Vec       coordinates;
8915   PetscReal detJ, J[9], refVol = 1.0;
8916   PetscReal vol;
8917   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8918 
8919   PetscFunctionBegin;
8920   PetscCall(DMGetDimension(dm, &dim));
8921   PetscCall(DMGetCoordinateDim(dm, &dE));
8922   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
8923   PetscCall(DMPlexGetDepth(dm, &depth));
8924   for (d = 0; d < dim; ++d) refVol *= 2.0;
8925   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8926   /* Make sure local coordinates are created, because that step is collective */
8927   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8928   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
8929   for (c = cStart; c < cEnd; ++c) {
8930     DMPolytopeType ct;
8931     PetscInt       unsplit;
8932     PetscBool      ignoreZeroVol = PETSC_FALSE;
8933 
8934     PetscCall(DMPlexGetCellType(dm, c, &ct));
8935     switch (ct) {
8936     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8937     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8938     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8939       ignoreZeroVol = PETSC_TRUE;
8940       break;
8941     default:
8942       break;
8943     }
8944     switch (ct) {
8945     case DM_POLYTOPE_TRI_PRISM:
8946     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8947     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8948     case DM_POLYTOPE_PYRAMID:
8949       continue;
8950     default:
8951       break;
8952     }
8953     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8954     if (unsplit) continue;
8955     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8956     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);
8957     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8958     /* This should work with periodicity since DG coordinates should be used */
8959     if (depth > 1) {
8960       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8961       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);
8962       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8963     }
8964   }
8965   PetscFunctionReturn(PETSC_SUCCESS);
8966 }
8967 
8968 /*@
8969   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8970 
8971   Collective on dm
8972 
8973   Input Parameters:
8974 + dm - The `DMPLEX` object
8975 . pointSF - The `PetscSF`, or NULL for `PointSF` attached to `DM`
8976 - allowExtraRoots - Flag to allow extra points not present in the `DM`
8977 
8978   Level: developer
8979 
8980   Notes:
8981   This is mainly intended for debugging/testing purposes.
8982 
8983   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8984 
8985   Extra roots can come from priodic cuts, where additional points appear on the boundary
8986 
8987 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
8988 @*/
8989 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
8990 {
8991   PetscInt           l, nleaves, nroots, overlap;
8992   const PetscInt    *locals;
8993   const PetscSFNode *remotes;
8994   PetscBool          distributed;
8995   MPI_Comm           comm;
8996   PetscMPIInt        rank;
8997 
8998   PetscFunctionBegin;
8999   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9000   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9001   else pointSF = dm->sf;
9002   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9003   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9004   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9005   {
9006     PetscMPIInt mpiFlag;
9007 
9008     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9009     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9010   }
9011   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9012   PetscCall(DMPlexIsDistributed(dm, &distributed));
9013   if (!distributed) {
9014     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);
9015     PetscFunctionReturn(PETSC_SUCCESS);
9016   }
9017   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);
9018   PetscCall(DMPlexGetOverlap(dm, &overlap));
9019 
9020   /* Check SF graph is compatible with DMPlex chart */
9021   {
9022     PetscInt pStart, pEnd, maxLeaf;
9023 
9024     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9025     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9026     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9027     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9028   }
9029 
9030   /* Check Point SF has no local points referenced */
9031   for (l = 0; l < nleaves; l++) {
9032     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);
9033   }
9034 
9035   /* Check there are no cells in interface */
9036   if (!overlap) {
9037     PetscInt cellHeight, cStart, cEnd;
9038 
9039     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9040     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9041     for (l = 0; l < nleaves; ++l) {
9042       const PetscInt point = locals ? locals[l] : l;
9043 
9044       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9045     }
9046   }
9047 
9048   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9049   {
9050     const PetscInt *rootdegree;
9051 
9052     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9053     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9054     for (l = 0; l < nleaves; ++l) {
9055       const PetscInt  point = locals ? locals[l] : l;
9056       const PetscInt *cone;
9057       PetscInt        coneSize, c, idx;
9058 
9059       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9060       PetscCall(DMPlexGetCone(dm, point, &cone));
9061       for (c = 0; c < coneSize; ++c) {
9062         if (!rootdegree[cone[c]]) {
9063           if (locals) {
9064             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9065           } else {
9066             idx = (cone[c] < nleaves) ? cone[c] : -1;
9067           }
9068           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9069         }
9070       }
9071     }
9072   }
9073   PetscFunctionReturn(PETSC_SUCCESS);
9074 }
9075 
9076 /*@
9077   DMPlexCheck - Perform various checks of Plex sanity
9078 
9079   Input Parameter:
9080 . dm - The `DMPLEX` object
9081 
9082   Level: developer
9083 
9084   Notes:
9085   This is a useful diagnostic when creating meshes programmatically.
9086 
9087   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
9088 
9089   Currently does not include DMPlexCheckCellShape().
9090 
9091 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, DMCreate(), DMSetFromOptions()
9092 @*/
9093 PetscErrorCode DMPlexCheck(DM dm)
9094 {
9095   PetscInt cellHeight;
9096 
9097   PetscFunctionBegin;
9098   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9099   PetscCall(DMPlexCheckSymmetry(dm));
9100   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9101   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9102   PetscCall(DMPlexCheckGeometry(dm));
9103   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9104   PetscCall(DMPlexCheckInterfaceCones(dm));
9105   PetscFunctionReturn(PETSC_SUCCESS);
9106 }
9107 
9108 typedef struct cell_stats {
9109   PetscReal min, max, sum, squaresum;
9110   PetscInt  count;
9111 } cell_stats_t;
9112 
9113 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9114 {
9115   PetscInt i, N = *len;
9116 
9117   for (i = 0; i < N; i++) {
9118     cell_stats_t *A = (cell_stats_t *)a;
9119     cell_stats_t *B = (cell_stats_t *)b;
9120 
9121     B->min = PetscMin(A->min, B->min);
9122     B->max = PetscMax(A->max, B->max);
9123     B->sum += A->sum;
9124     B->squaresum += A->squaresum;
9125     B->count += A->count;
9126   }
9127 }
9128 
9129 /*@
9130   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9131 
9132   Collective on dm
9133 
9134   Input Parameters:
9135 + dm        - The `DMPLEX` object
9136 . output    - If true, statistics will be displayed on stdout
9137 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9138 
9139   Level: developer
9140 
9141   Notes:
9142   This is mainly intended for debugging/testing purposes.
9143 
9144   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9145 
9146 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9147 @*/
9148 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9149 {
9150   DM           dmCoarse;
9151   cell_stats_t stats, globalStats;
9152   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9153   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9154   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9155   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9156   PetscMPIInt  rank, size;
9157 
9158   PetscFunctionBegin;
9159   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9160   stats.min = PETSC_MAX_REAL;
9161   stats.max = PETSC_MIN_REAL;
9162   stats.sum = stats.squaresum = 0.;
9163   stats.count                 = 0;
9164 
9165   PetscCallMPI(MPI_Comm_size(comm, &size));
9166   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9167   PetscCall(DMGetCoordinateDim(dm, &cdim));
9168   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9169   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9170   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9171   for (c = cStart; c < cEnd; c++) {
9172     PetscInt  i;
9173     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9174 
9175     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9176     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9177     for (i = 0; i < PetscSqr(cdim); ++i) {
9178       frobJ += J[i] * J[i];
9179       frobInvJ += invJ[i] * invJ[i];
9180     }
9181     cond2 = frobJ * frobInvJ;
9182     cond  = PetscSqrtReal(cond2);
9183 
9184     stats.min = PetscMin(stats.min, cond);
9185     stats.max = PetscMax(stats.max, cond);
9186     stats.sum += cond;
9187     stats.squaresum += cond2;
9188     stats.count++;
9189     if (output && cond > limit) {
9190       PetscSection coordSection;
9191       Vec          coordsLocal;
9192       PetscScalar *coords = NULL;
9193       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9194 
9195       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9196       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9197       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9198       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9199       for (i = 0; i < Nv / cdim; ++i) {
9200         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9201         for (d = 0; d < cdim; ++d) {
9202           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9203           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9204         }
9205         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9206       }
9207       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9208       for (cl = 0; cl < clSize * 2; cl += 2) {
9209         const PetscInt edge = closure[cl];
9210 
9211         if ((edge >= eStart) && (edge < eEnd)) {
9212           PetscReal len;
9213 
9214           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9215           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9216         }
9217       }
9218       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9219       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9220     }
9221   }
9222   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9223 
9224   if (size > 1) {
9225     PetscMPIInt  blockLengths[2] = {4, 1};
9226     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9227     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9228     MPI_Op       statReduce;
9229 
9230     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9231     PetscCallMPI(MPI_Type_commit(&statType));
9232     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9233     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9234     PetscCallMPI(MPI_Op_free(&statReduce));
9235     PetscCallMPI(MPI_Type_free(&statType));
9236   } else {
9237     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9238   }
9239   if (rank == 0) {
9240     count = globalStats.count;
9241     min   = globalStats.min;
9242     max   = globalStats.max;
9243     mean  = globalStats.sum / globalStats.count;
9244     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9245   }
9246 
9247   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));
9248   PetscCall(PetscFree2(J, invJ));
9249 
9250   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9251   if (dmCoarse) {
9252     PetscBool isplex;
9253 
9254     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9255     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9256   }
9257   PetscFunctionReturn(PETSC_SUCCESS);
9258 }
9259 
9260 /*@
9261   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9262   orthogonal quality below given tolerance.
9263 
9264   Collective on dm
9265 
9266   Input Parameters:
9267 + dm   - The `DMPLEX` object
9268 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9269 - atol - [0, 1] Absolute tolerance for tagging cells.
9270 
9271   Output Parameters:
9272 + OrthQual      - Vec containing orthogonal quality per cell
9273 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9274 
9275   Options Database Keys:
9276 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9277 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9278 
9279   Level: intermediate
9280 
9281   Notes:
9282   Orthogonal quality is given by the following formula:
9283 
9284   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9285 
9286   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
9287   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9288   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9289   calculating the cosine of the angle between these vectors.
9290 
9291   Orthogonal quality ranges from 1 (best) to 0 (worst).
9292 
9293   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9294   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9295 
9296   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9297 
9298 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9299 @*/
9300 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9301 {
9302   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9303   PetscInt              *idx;
9304   PetscScalar           *oqVals;
9305   const PetscScalar     *cellGeomArr, *faceGeomArr;
9306   PetscReal             *ci, *fi, *Ai;
9307   MPI_Comm               comm;
9308   Vec                    cellgeom, facegeom;
9309   DM                     dmFace, dmCell;
9310   IS                     glob;
9311   ISLocalToGlobalMapping ltog;
9312   PetscViewer            vwr;
9313 
9314   PetscFunctionBegin;
9315   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9316   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9317   PetscValidPointer(OrthQual, 4);
9318   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9319   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9320   PetscCall(DMGetDimension(dm, &nc));
9321   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9322   {
9323     DMPlexInterpolatedFlag interpFlag;
9324 
9325     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9326     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9327       PetscMPIInt rank;
9328 
9329       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9330       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9331     }
9332   }
9333   if (OrthQualLabel) {
9334     PetscValidPointer(OrthQualLabel, 5);
9335     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9336     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9337   } else {
9338     *OrthQualLabel = NULL;
9339   }
9340   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9341   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9342   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9343   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9344   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9345   PetscCall(VecCreate(comm, OrthQual));
9346   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9347   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9348   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9349   PetscCall(VecSetUp(*OrthQual));
9350   PetscCall(ISDestroy(&glob));
9351   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9352   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9353   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9354   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9355   PetscCall(VecGetDM(cellgeom, &dmCell));
9356   PetscCall(VecGetDM(facegeom, &dmFace));
9357   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9358   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9359     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9360     PetscInt         cellarr[2], *adj = NULL;
9361     PetscScalar     *cArr, *fArr;
9362     PetscReal        minvalc = 1.0, minvalf = 1.0;
9363     PetscFVCellGeom *cg;
9364 
9365     idx[cellIter] = cell - cStart;
9366     cellarr[0]    = cell;
9367     /* Make indexing into cellGeom easier */
9368     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9369     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9370     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9371     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9372     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9373       PetscInt         i;
9374       const PetscInt   neigh  = adj[cellneigh];
9375       PetscReal        normci = 0, normfi = 0, normai = 0;
9376       PetscFVCellGeom *cgneigh;
9377       PetscFVFaceGeom *fg;
9378 
9379       /* Don't count ourselves in the neighbor list */
9380       if (neigh == cell) continue;
9381       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9382       cellarr[1] = neigh;
9383       {
9384         PetscInt        numcovpts;
9385         const PetscInt *covpts;
9386 
9387         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9388         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9389         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9390       }
9391 
9392       /* Compute c_i, f_i and their norms */
9393       for (i = 0; i < nc; i++) {
9394         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9395         fi[i] = fg->centroid[i] - cg->centroid[i];
9396         Ai[i] = fg->normal[i];
9397         normci += PetscPowReal(ci[i], 2);
9398         normfi += PetscPowReal(fi[i], 2);
9399         normai += PetscPowReal(Ai[i], 2);
9400       }
9401       normci = PetscSqrtReal(normci);
9402       normfi = PetscSqrtReal(normfi);
9403       normai = PetscSqrtReal(normai);
9404 
9405       /* Normalize and compute for each face-cell-normal pair */
9406       for (i = 0; i < nc; i++) {
9407         ci[i] = ci[i] / normci;
9408         fi[i] = fi[i] / normfi;
9409         Ai[i] = Ai[i] / normai;
9410         /* PetscAbs because I don't know if normals are guaranteed to point out */
9411         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9412         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9413       }
9414       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9415       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9416     }
9417     PetscCall(PetscFree(adj));
9418     PetscCall(PetscFree2(cArr, fArr));
9419     /* Defer to cell if they're equal */
9420     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9421     if (OrthQualLabel) {
9422       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9423     }
9424   }
9425   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9426   PetscCall(VecAssemblyBegin(*OrthQual));
9427   PetscCall(VecAssemblyEnd(*OrthQual));
9428   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9429   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9430   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9431   if (OrthQualLabel) {
9432     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9433   }
9434   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9435   PetscCall(PetscViewerDestroy(&vwr));
9436   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9437   PetscFunctionReturn(PETSC_SUCCESS);
9438 }
9439 
9440 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9441  * interpolator construction */
9442 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9443 {
9444   PetscSection section, newSection, gsection;
9445   PetscSF      sf;
9446   PetscBool    hasConstraints, ghasConstraints;
9447 
9448   PetscFunctionBegin;
9449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9450   PetscValidPointer(odm, 2);
9451   PetscCall(DMGetLocalSection(dm, &section));
9452   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9453   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9454   if (!ghasConstraints) {
9455     PetscCall(PetscObjectReference((PetscObject)dm));
9456     *odm = dm;
9457     PetscFunctionReturn(PETSC_SUCCESS);
9458   }
9459   PetscCall(DMClone(dm, odm));
9460   PetscCall(DMCopyFields(dm, *odm));
9461   PetscCall(DMGetLocalSection(*odm, &newSection));
9462   PetscCall(DMGetPointSF(*odm, &sf));
9463   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9464   PetscCall(DMSetGlobalSection(*odm, gsection));
9465   PetscCall(PetscSectionDestroy(&gsection));
9466   PetscFunctionReturn(PETSC_SUCCESS);
9467 }
9468 
9469 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9470 {
9471   DM        dmco, dmfo;
9472   Mat       interpo;
9473   Vec       rscale;
9474   Vec       cglobalo, clocal;
9475   Vec       fglobal, fglobalo, flocal;
9476   PetscBool regular;
9477 
9478   PetscFunctionBegin;
9479   PetscCall(DMGetFullDM(dmc, &dmco));
9480   PetscCall(DMGetFullDM(dmf, &dmfo));
9481   PetscCall(DMSetCoarseDM(dmfo, dmco));
9482   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9483   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9484   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9485   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9486   PetscCall(DMCreateLocalVector(dmc, &clocal));
9487   PetscCall(VecSet(cglobalo, 0.));
9488   PetscCall(VecSet(clocal, 0.));
9489   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9490   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9491   PetscCall(DMCreateLocalVector(dmf, &flocal));
9492   PetscCall(VecSet(fglobal, 0.));
9493   PetscCall(VecSet(fglobalo, 0.));
9494   PetscCall(VecSet(flocal, 0.));
9495   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9496   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9497   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9498   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9499   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9500   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9501   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9502   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9503   *shift = fglobal;
9504   PetscCall(VecDestroy(&flocal));
9505   PetscCall(VecDestroy(&fglobalo));
9506   PetscCall(VecDestroy(&clocal));
9507   PetscCall(VecDestroy(&cglobalo));
9508   PetscCall(VecDestroy(&rscale));
9509   PetscCall(MatDestroy(&interpo));
9510   PetscCall(DMDestroy(&dmfo));
9511   PetscCall(DMDestroy(&dmco));
9512   PetscFunctionReturn(PETSC_SUCCESS);
9513 }
9514 
9515 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9516 {
9517   PetscObject shifto;
9518   Vec         shift;
9519 
9520   PetscFunctionBegin;
9521   if (!interp) {
9522     Vec rscale;
9523 
9524     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9525     PetscCall(VecDestroy(&rscale));
9526   } else {
9527     PetscCall(PetscObjectReference((PetscObject)interp));
9528   }
9529   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9530   if (!shifto) {
9531     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9532     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9533     shifto = (PetscObject)shift;
9534     PetscCall(VecDestroy(&shift));
9535   }
9536   shift = (Vec)shifto;
9537   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9538   PetscCall(VecAXPY(fineSol, 1.0, shift));
9539   PetscCall(MatDestroy(&interp));
9540   PetscFunctionReturn(PETSC_SUCCESS);
9541 }
9542 
9543 /* Pointwise interpolation
9544      Just code FEM for now
9545      u^f = I u^c
9546      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9547      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9548      I_{ij} = psi^f_i phi^c_j
9549 */
9550 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9551 {
9552   PetscSection gsc, gsf;
9553   PetscInt     m, n;
9554   void        *ctx;
9555   DM           cdm;
9556   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9557 
9558   PetscFunctionBegin;
9559   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9560   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9561   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9562   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9563 
9564   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9565   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9566   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9567   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9568   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9569 
9570   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9571   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9572   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9573   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9574   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9575   if (scaling) {
9576     /* Use naive scaling */
9577     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9578   }
9579   PetscFunctionReturn(PETSC_SUCCESS);
9580 }
9581 
9582 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9583 {
9584   VecScatter ctx;
9585 
9586   PetscFunctionBegin;
9587   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9588   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9589   PetscCall(VecScatterDestroy(&ctx));
9590   PetscFunctionReturn(PETSC_SUCCESS);
9591 }
9592 
9593 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[])
9594 {
9595   const PetscInt Nc = uOff[1] - uOff[0];
9596   PetscInt       c;
9597   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9598 }
9599 
9600 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9601 {
9602   DM           dmc;
9603   PetscDS      ds;
9604   Vec          ones, locmass;
9605   IS           cellIS;
9606   PetscFormKey key;
9607   PetscInt     depth;
9608 
9609   PetscFunctionBegin;
9610   PetscCall(DMClone(dm, &dmc));
9611   PetscCall(DMCopyDisc(dm, dmc));
9612   PetscCall(DMGetDS(dmc, &ds));
9613   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9614   PetscCall(DMCreateGlobalVector(dmc, mass));
9615   PetscCall(DMGetLocalVector(dmc, &ones));
9616   PetscCall(DMGetLocalVector(dmc, &locmass));
9617   PetscCall(DMPlexGetDepth(dmc, &depth));
9618   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9619   PetscCall(VecSet(locmass, 0.0));
9620   PetscCall(VecSet(ones, 1.0));
9621   key.label = NULL;
9622   key.value = 0;
9623   key.field = 0;
9624   key.part  = 0;
9625   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9626   PetscCall(ISDestroy(&cellIS));
9627   PetscCall(VecSet(*mass, 0.0));
9628   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9629   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9630   PetscCall(DMRestoreLocalVector(dmc, &ones));
9631   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9632   PetscCall(DMDestroy(&dmc));
9633   PetscFunctionReturn(PETSC_SUCCESS);
9634 }
9635 
9636 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9637 {
9638   PetscSection gsc, gsf;
9639   PetscInt     m, n;
9640   void        *ctx;
9641   DM           cdm;
9642   PetscBool    regular;
9643 
9644   PetscFunctionBegin;
9645   if (dmFine == dmCoarse) {
9646     DM            dmc;
9647     PetscDS       ds;
9648     PetscWeakForm wf;
9649     Vec           u;
9650     IS            cellIS;
9651     PetscFormKey  key;
9652     PetscInt      depth;
9653 
9654     PetscCall(DMClone(dmFine, &dmc));
9655     PetscCall(DMCopyDisc(dmFine, dmc));
9656     PetscCall(DMGetDS(dmc, &ds));
9657     PetscCall(PetscDSGetWeakForm(ds, &wf));
9658     PetscCall(PetscWeakFormClear(wf));
9659     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9660     PetscCall(DMCreateMatrix(dmc, mass));
9661     PetscCall(DMGetLocalVector(dmc, &u));
9662     PetscCall(DMPlexGetDepth(dmc, &depth));
9663     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9664     PetscCall(MatZeroEntries(*mass));
9665     key.label = NULL;
9666     key.value = 0;
9667     key.field = 0;
9668     key.part  = 0;
9669     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9670     PetscCall(ISDestroy(&cellIS));
9671     PetscCall(DMRestoreLocalVector(dmc, &u));
9672     PetscCall(DMDestroy(&dmc));
9673   } else {
9674     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9675     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9676     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9677     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9678 
9679     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9680     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9681     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9682     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9683 
9684     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9685     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9686     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9687     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9688   }
9689   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9690   PetscFunctionReturn(PETSC_SUCCESS);
9691 }
9692 
9693 /*@
9694   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9695 
9696   Input Parameter:
9697 . dm - The `DMPLEX` object
9698 
9699   Output Parameter:
9700 . regular - The flag
9701 
9702   Level: intermediate
9703 
9704 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9705 @*/
9706 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9707 {
9708   PetscFunctionBegin;
9709   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9710   PetscValidBoolPointer(regular, 2);
9711   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9712   PetscFunctionReturn(PETSC_SUCCESS);
9713 }
9714 
9715 /*@
9716   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9717 
9718   Input Parameters:
9719 + dm - The `DMPLEX` object
9720 - regular - The flag
9721 
9722   Level: intermediate
9723 
9724 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9725 @*/
9726 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9727 {
9728   PetscFunctionBegin;
9729   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9730   ((DM_Plex *)dm->data)->regularRefinement = regular;
9731   PetscFunctionReturn(PETSC_SUCCESS);
9732 }
9733 
9734 /*@
9735   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9736   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9737 
9738   Not Collective
9739 
9740   Input Parameter:
9741 . dm - The `DMPLEX` object
9742 
9743   Output Parameters:
9744 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9745 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9746 
9747   Level: intermediate
9748 
9749 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9750 @*/
9751 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9752 {
9753   DM_Plex *plex = (DM_Plex *)dm->data;
9754 
9755   PetscFunctionBegin;
9756   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9757   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9758   if (anchorSection) *anchorSection = plex->anchorSection;
9759   if (anchorIS) *anchorIS = plex->anchorIS;
9760   PetscFunctionReturn(PETSC_SUCCESS);
9761 }
9762 
9763 /*@
9764   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9765   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9766   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9767 
9768   Collective on dm
9769 
9770   Input Parameters:
9771 + dm - The `DMPLEX` object
9772 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9773                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9774 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9775 
9776   Level: intermediate
9777 
9778   Notes:
9779   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9780   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9781 
9782   The reference counts of anchorSection and anchorIS are incremented.
9783 
9784 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9785 @*/
9786 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9787 {
9788   DM_Plex    *plex = (DM_Plex *)dm->data;
9789   PetscMPIInt result;
9790 
9791   PetscFunctionBegin;
9792   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9793   if (anchorSection) {
9794     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9795     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9796     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9797   }
9798   if (anchorIS) {
9799     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9800     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9801     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9802   }
9803 
9804   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9805   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9806   plex->anchorSection = anchorSection;
9807 
9808   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9809   PetscCall(ISDestroy(&plex->anchorIS));
9810   plex->anchorIS = anchorIS;
9811 
9812   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9813     PetscInt        size, a, pStart, pEnd;
9814     const PetscInt *anchors;
9815 
9816     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9817     PetscCall(ISGetLocalSize(anchorIS, &size));
9818     PetscCall(ISGetIndices(anchorIS, &anchors));
9819     for (a = 0; a < size; a++) {
9820       PetscInt p;
9821 
9822       p = anchors[a];
9823       if (p >= pStart && p < pEnd) {
9824         PetscInt dof;
9825 
9826         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9827         if (dof) {
9828           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9829           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9830         }
9831       }
9832     }
9833     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9834   }
9835   /* reset the generic constraints */
9836   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9837   PetscFunctionReturn(PETSC_SUCCESS);
9838 }
9839 
9840 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9841 {
9842   PetscSection anchorSection;
9843   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9844 
9845   PetscFunctionBegin;
9846   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9847   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9848   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9849   PetscCall(PetscSectionGetNumFields(section, &numFields));
9850   if (numFields) {
9851     PetscInt f;
9852     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9853 
9854     for (f = 0; f < numFields; f++) {
9855       PetscInt numComp;
9856 
9857       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9858       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9859     }
9860   }
9861   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9862   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9863   pStart = PetscMax(pStart, sStart);
9864   pEnd   = PetscMin(pEnd, sEnd);
9865   pEnd   = PetscMax(pStart, pEnd);
9866   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9867   for (p = pStart; p < pEnd; p++) {
9868     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9869     if (dof) {
9870       PetscCall(PetscSectionGetDof(section, p, &dof));
9871       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9872       for (f = 0; f < numFields; f++) {
9873         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9874         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9875       }
9876     }
9877   }
9878   PetscCall(PetscSectionSetUp(*cSec));
9879   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9880   PetscFunctionReturn(PETSC_SUCCESS);
9881 }
9882 
9883 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9884 {
9885   PetscSection    aSec;
9886   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9887   const PetscInt *anchors;
9888   PetscInt        numFields, f;
9889   IS              aIS;
9890   MatType         mtype;
9891   PetscBool       iscuda, iskokkos;
9892 
9893   PetscFunctionBegin;
9894   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9895   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9896   PetscCall(PetscSectionGetStorageSize(section, &n));
9897   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9898   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9899   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9900   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9901   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9902   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9903   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9904   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9905   else mtype = MATSEQAIJ;
9906   PetscCall(MatSetType(*cMat, mtype));
9907   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9908   PetscCall(ISGetIndices(aIS, &anchors));
9909   /* cSec will be a subset of aSec and section */
9910   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9911   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9912   PetscCall(PetscMalloc1(m + 1, &i));
9913   i[0] = 0;
9914   PetscCall(PetscSectionGetNumFields(section, &numFields));
9915   for (p = pStart; p < pEnd; p++) {
9916     PetscInt rDof, rOff, r;
9917 
9918     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9919     if (!rDof) continue;
9920     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9921     if (numFields) {
9922       for (f = 0; f < numFields; f++) {
9923         annz = 0;
9924         for (r = 0; r < rDof; r++) {
9925           a = anchors[rOff + r];
9926           if (a < sStart || a >= sEnd) continue;
9927           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9928           annz += aDof;
9929         }
9930         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9931         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9932         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9933       }
9934     } else {
9935       annz = 0;
9936       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9937       for (q = 0; q < dof; q++) {
9938         a = anchors[rOff + q];
9939         if (a < sStart || a >= sEnd) continue;
9940         PetscCall(PetscSectionGetDof(section, a, &aDof));
9941         annz += aDof;
9942       }
9943       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9944       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9945       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9946     }
9947   }
9948   nnz = i[m];
9949   PetscCall(PetscMalloc1(nnz, &j));
9950   offset = 0;
9951   for (p = pStart; p < pEnd; p++) {
9952     if (numFields) {
9953       for (f = 0; f < numFields; f++) {
9954         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9955         for (q = 0; q < dof; q++) {
9956           PetscInt rDof, rOff, r;
9957           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9958           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9959           for (r = 0; r < rDof; r++) {
9960             PetscInt s;
9961 
9962             a = anchors[rOff + r];
9963             if (a < sStart || a >= sEnd) continue;
9964             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9965             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9966             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9967           }
9968         }
9969       }
9970     } else {
9971       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9972       for (q = 0; q < dof; q++) {
9973         PetscInt rDof, rOff, r;
9974         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9975         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9976         for (r = 0; r < rDof; r++) {
9977           PetscInt s;
9978 
9979           a = anchors[rOff + r];
9980           if (a < sStart || a >= sEnd) continue;
9981           PetscCall(PetscSectionGetDof(section, a, &aDof));
9982           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9983           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9984         }
9985       }
9986     }
9987   }
9988   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9989   PetscCall(PetscFree(i));
9990   PetscCall(PetscFree(j));
9991   PetscCall(ISRestoreIndices(aIS, &anchors));
9992   PetscFunctionReturn(PETSC_SUCCESS);
9993 }
9994 
9995 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9996 {
9997   DM_Plex     *plex = (DM_Plex *)dm->data;
9998   PetscSection anchorSection, section, cSec;
9999   Mat          cMat;
10000 
10001   PetscFunctionBegin;
10002   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10003   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10004   if (anchorSection) {
10005     PetscInt Nf;
10006 
10007     PetscCall(DMGetLocalSection(dm, &section));
10008     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10009     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10010     PetscCall(DMGetNumFields(dm, &Nf));
10011     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10012     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10013     PetscCall(PetscSectionDestroy(&cSec));
10014     PetscCall(MatDestroy(&cMat));
10015   }
10016   PetscFunctionReturn(PETSC_SUCCESS);
10017 }
10018 
10019 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10020 {
10021   IS           subis;
10022   PetscSection section, subsection;
10023 
10024   PetscFunctionBegin;
10025   PetscCall(DMGetLocalSection(dm, &section));
10026   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10027   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10028   /* Create subdomain */
10029   PetscCall(DMPlexFilter(dm, label, value, subdm));
10030   /* Create submodel */
10031   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10032   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10033   PetscCall(DMSetLocalSection(*subdm, subsection));
10034   PetscCall(PetscSectionDestroy(&subsection));
10035   PetscCall(DMCopyDisc(dm, *subdm));
10036   /* Create map from submodel to global model */
10037   if (is) {
10038     PetscSection    sectionGlobal, subsectionGlobal;
10039     IS              spIS;
10040     const PetscInt *spmap;
10041     PetscInt       *subIndices;
10042     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10043     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10044 
10045     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10046     PetscCall(ISGetIndices(spIS, &spmap));
10047     PetscCall(PetscSectionGetNumFields(section, &Nf));
10048     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10049     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10050     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10051     for (p = pStart; p < pEnd; ++p) {
10052       PetscInt gdof, pSubSize = 0;
10053 
10054       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10055       if (gdof > 0) {
10056         for (f = 0; f < Nf; ++f) {
10057           PetscInt fdof, fcdof;
10058 
10059           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10060           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10061           pSubSize += fdof - fcdof;
10062         }
10063         subSize += pSubSize;
10064         if (pSubSize) {
10065           if (bs < 0) {
10066             bs = pSubSize;
10067           } else if (bs != pSubSize) {
10068             /* Layout does not admit a pointwise block size */
10069             bs = 1;
10070           }
10071         }
10072       }
10073     }
10074     /* Must have same blocksize on all procs (some might have no points) */
10075     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10076     bsLocal[1] = bs;
10077     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10078     if (bsMinMax[0] != bsMinMax[1]) {
10079       bs = 1;
10080     } else {
10081       bs = bsMinMax[0];
10082     }
10083     PetscCall(PetscMalloc1(subSize, &subIndices));
10084     for (p = pStart; p < pEnd; ++p) {
10085       PetscInt gdof, goff;
10086 
10087       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10088       if (gdof > 0) {
10089         const PetscInt point = spmap[p];
10090 
10091         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10092         for (f = 0; f < Nf; ++f) {
10093           PetscInt fdof, fcdof, fc, f2, poff = 0;
10094 
10095           /* Can get rid of this loop by storing field information in the global section */
10096           for (f2 = 0; f2 < f; ++f2) {
10097             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10098             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10099             poff += fdof - fcdof;
10100           }
10101           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10102           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10103           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10104         }
10105       }
10106     }
10107     PetscCall(ISRestoreIndices(spIS, &spmap));
10108     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10109     if (bs > 1) {
10110       /* We need to check that the block size does not come from non-contiguous fields */
10111       PetscInt i, j, set = 1;
10112       for (i = 0; i < subSize; i += bs) {
10113         for (j = 0; j < bs; ++j) {
10114           if (subIndices[i + j] != subIndices[i] + j) {
10115             set = 0;
10116             break;
10117           }
10118         }
10119       }
10120       if (set) PetscCall(ISSetBlockSize(*is, bs));
10121     }
10122     /* Attach nullspace */
10123     for (f = 0; f < Nf; ++f) {
10124       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10125       if ((*subdm)->nullspaceConstructors[f]) break;
10126     }
10127     if (f < Nf) {
10128       MatNullSpace nullSpace;
10129       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10130 
10131       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10132       PetscCall(MatNullSpaceDestroy(&nullSpace));
10133     }
10134   }
10135   PetscFunctionReturn(PETSC_SUCCESS);
10136 }
10137 
10138 /*@
10139   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10140 
10141   Input Parameters:
10142 + dm - The `DM`
10143 - dummy - unused argument
10144 
10145   Options Database Key:
10146 . -dm_plex_monitor_throughput - Activate the monitor
10147 
10148   Level: developer
10149 
10150 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10151 @*/
10152 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10153 {
10154 #if defined(PETSC_USE_LOG)
10155   PetscStageLog      stageLog;
10156   PetscLogEvent      event;
10157   PetscLogStage      stage;
10158   PetscEventPerfInfo eventInfo;
10159   PetscReal          cellRate, flopRate;
10160   PetscInt           cStart, cEnd, Nf, N;
10161   const char        *name;
10162 #endif
10163 
10164   PetscFunctionBegin;
10165   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10166 #if defined(PETSC_USE_LOG)
10167   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10168   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10169   PetscCall(DMGetNumFields(dm, &Nf));
10170   PetscCall(PetscLogGetStageLog(&stageLog));
10171   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10172   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10173   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10174   N        = (cEnd - cStart) * Nf * eventInfo.count;
10175   flopRate = eventInfo.flops / eventInfo.time;
10176   cellRate = N / eventInfo.time;
10177   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)));
10178 #else
10179   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10180 #endif
10181   PetscFunctionReturn(PETSC_SUCCESS);
10182 }
10183