xref: /petsc/src/dm/impls/plex/plex.c (revision 73fdd05bb67e49f40fd8fd311695ff6fdf0b9b8a)
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(PetscStrcpy(tmpname, vname));
202         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
203         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
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 *coords, *array;
253   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
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(VecGetArrayRead(coordinates, &coords));
278   for (c = 0; c < N; c += dim) {
279     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
280     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
281     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
282     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
283   }
284   PetscCall(VecRestoreArrayRead(coordinates, &coords));
285   PetscCall(PetscDrawClear(draw));
286 
287   /* Could implement something like DMDASelectFields() */
288   for (f = 0; f < Nf; ++f) {
289     DM          fdm = dm;
290     Vec         fv  = v;
291     IS          fis;
292     char        prefix[PETSC_MAX_PATH_LEN];
293     const char *fname;
294 
295     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
296     PetscCall(PetscSectionGetFieldName(s, f, &fname));
297 
298     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
299     else prefix[0] = '\0';
300     if (Nf > 1) {
301       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
302       PetscCall(VecGetSubVector(v, fis, &fv));
303       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
304       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
305     }
306     for (comp = 0; comp < Nc; ++comp, ++w) {
307       PetscInt nmax = 2;
308 
309       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
310       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
311       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
312       PetscCall(PetscDrawSetTitle(draw, title));
313 
314       /* TODO Get max and min only for this component */
315       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
316       if (!flg) {
317         PetscCall(VecMin(fv, NULL, &vbound[0]));
318         PetscCall(VecMax(fv, NULL, &vbound[1]));
319         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
320       }
321       PetscCall(PetscDrawGetPopup(draw, &popup));
322       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
323       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
324 
325       PetscCall(VecGetArrayRead(fv, &array));
326       for (c = cStart; c < cEnd; ++c) {
327         PetscScalar *coords = NULL, *a   = NULL;
328         PetscInt     numCoords, color[4] = {-1, -1, -1, -1};
329 
330         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
331         if (a) {
332           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
333           color[1] = color[2] = color[3] = color[0];
334         } else {
335           PetscScalar *vals = NULL;
336           PetscInt     numVals, va;
337 
338           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
339           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);
340           switch (numVals / Nc) {
341           case 3: /* P1 Triangle */
342           case 4: /* P1 Quadrangle */
343             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
344             break;
345           case 6: /* P2 Triangle */
346           case 8: /* P2 Quadrangle */
347             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
348             break;
349           default:
350             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
351           }
352           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
353         }
354         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
355         switch (numCoords) {
356         case 6:
357         case 12: /* Localized triangle */
358           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]));
359           break;
360         case 8:
361         case 16: /* Localized quadrilateral */
362           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]));
363           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]));
364           break;
365         default:
366           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
367         }
368         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
369       }
370       PetscCall(VecRestoreArrayRead(fv, &array));
371       PetscCall(PetscDrawFlush(draw));
372       PetscCall(PetscDrawPause(draw));
373       PetscCall(PetscDrawSave(draw));
374     }
375     if (Nf > 1) {
376       PetscCall(VecRestoreSubVector(v, fis, &fv));
377       PetscCall(ISDestroy(&fis));
378       PetscCall(DMDestroy(&fdm));
379     }
380   }
381   PetscFunctionReturn(PETSC_SUCCESS);
382 }
383 
384 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
385 {
386   DM        dm;
387   PetscDraw draw;
388   PetscInt  dim;
389   PetscBool isnull;
390 
391   PetscFunctionBegin;
392   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
393   PetscCall(PetscDrawIsNull(draw, &isnull));
394   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
395 
396   PetscCall(VecGetDM(v, &dm));
397   PetscCall(DMGetCoordinateDim(dm, &dim));
398   switch (dim) {
399   case 1:
400     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
401     break;
402   case 2:
403     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
404     break;
405   default:
406     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
407   }
408   PetscFunctionReturn(PETSC_SUCCESS);
409 }
410 
411 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
412 {
413   DM                      dm;
414   Vec                     locv;
415   const char             *name;
416   PetscSection            section;
417   PetscInt                pStart, pEnd;
418   PetscInt                numFields;
419   PetscViewerVTKFieldType ft;
420 
421   PetscFunctionBegin;
422   PetscCall(VecGetDM(v, &dm));
423   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
424   PetscCall(PetscObjectGetName((PetscObject)v, &name));
425   PetscCall(PetscObjectSetName((PetscObject)locv, name));
426   PetscCall(VecCopy(v, locv));
427   PetscCall(DMGetLocalSection(dm, &section));
428   PetscCall(PetscSectionGetNumFields(section, &numFields));
429   if (!numFields) {
430     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
431     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
432   } else {
433     PetscInt f;
434 
435     for (f = 0; f < numFields; f++) {
436       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
437       if (ft == PETSC_VTK_INVALID) continue;
438       PetscCall(PetscObjectReference((PetscObject)locv));
439       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
440     }
441     PetscCall(VecDestroy(&locv));
442   }
443   PetscFunctionReturn(PETSC_SUCCESS);
444 }
445 
446 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
447 {
448   DM        dm;
449   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
450 
451   PetscFunctionBegin;
452   PetscCall(VecGetDM(v, &dm));
453   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
454   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
455   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
456   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
457   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
458   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
459   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
460     PetscInt    i, numFields;
461     PetscObject fe;
462     PetscBool   fem  = PETSC_FALSE;
463     Vec         locv = v;
464     const char *name;
465     PetscInt    step;
466     PetscReal   time;
467 
468     PetscCall(DMGetNumFields(dm, &numFields));
469     for (i = 0; i < numFields; i++) {
470       PetscCall(DMGetField(dm, i, NULL, &fe));
471       if (fe->classid == PETSCFE_CLASSID) {
472         fem = PETSC_TRUE;
473         break;
474       }
475     }
476     if (fem) {
477       PetscObject isZero;
478 
479       PetscCall(DMGetLocalVector(dm, &locv));
480       PetscCall(PetscObjectGetName((PetscObject)v, &name));
481       PetscCall(PetscObjectSetName((PetscObject)locv, name));
482       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
483       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
484       PetscCall(VecCopy(v, locv));
485       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
486       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
487     }
488     if (isvtk) {
489       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
490     } else if (ishdf5) {
491 #if defined(PETSC_HAVE_HDF5)
492       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
493 #else
494       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
495 #endif
496     } else if (isdraw) {
497       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
498     } else if (isglvis) {
499       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
500       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
501       PetscCall(VecView_GLVis(locv, viewer));
502     } else if (iscgns) {
503 #if defined(PETSC_HAVE_CGNS)
504       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
505 #else
506       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
507 #endif
508     }
509     if (fem) {
510       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
511       PetscCall(DMRestoreLocalVector(dm, &locv));
512     }
513   } else {
514     PetscBool isseq;
515 
516     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
517     if (isseq) PetscCall(VecView_Seq(v, viewer));
518     else PetscCall(VecView_MPI(v, viewer));
519   }
520   PetscFunctionReturn(PETSC_SUCCESS);
521 }
522 
523 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
524 {
525   DM        dm;
526   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
527 
528   PetscFunctionBegin;
529   PetscCall(VecGetDM(v, &dm));
530   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
532   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
533   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
534   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
535   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
536   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
537   if (isvtk || isdraw || isglvis || iscgns) {
538     Vec         locv;
539     PetscObject isZero;
540     const char *name;
541 
542     PetscCall(DMGetLocalVector(dm, &locv));
543     PetscCall(PetscObjectGetName((PetscObject)v, &name));
544     PetscCall(PetscObjectSetName((PetscObject)locv, name));
545     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
546     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
547     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
548     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
549     PetscCall(VecView_Plex_Local(locv, viewer));
550     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
551     PetscCall(DMRestoreLocalVector(dm, &locv));
552   } else if (ishdf5) {
553 #if defined(PETSC_HAVE_HDF5)
554     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
555 #else
556     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
557 #endif
558   } else if (isexodusii) {
559 #if defined(PETSC_HAVE_EXODUSII)
560     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
561 #else
562     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
563 #endif
564   } else {
565     PetscBool isseq;
566 
567     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
568     if (isseq) PetscCall(VecView_Seq(v, viewer));
569     else PetscCall(VecView_MPI(v, viewer));
570   }
571   PetscFunctionReturn(PETSC_SUCCESS);
572 }
573 
574 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
575 {
576   DM                dm;
577   MPI_Comm          comm;
578   PetscViewerFormat format;
579   Vec               v;
580   PetscBool         isvtk, ishdf5;
581 
582   PetscFunctionBegin;
583   PetscCall(VecGetDM(originalv, &dm));
584   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
585   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
586   PetscCall(PetscViewerGetFormat(viewer, &format));
587   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
588   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
589   if (format == PETSC_VIEWER_NATIVE) {
590     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
591     /* this need a better fix */
592     if (dm->useNatural) {
593       if (dm->sfNatural) {
594         const char *vecname;
595         PetscInt    n, nroots;
596 
597         PetscCall(VecGetLocalSize(originalv, &n));
598         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
599         if (n == nroots) {
600           PetscCall(DMPlexCreateNaturalVector(dm, &v));
601           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
602           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
603           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
604           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
605         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
606       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
607     } else v = originalv;
608   } else v = originalv;
609 
610   if (ishdf5) {
611 #if defined(PETSC_HAVE_HDF5)
612     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
613 #else
614     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
615 #endif
616   } else if (isvtk) {
617     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
618   } else {
619     PetscBool isseq;
620 
621     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
622     if (isseq) PetscCall(VecView_Seq(v, viewer));
623     else PetscCall(VecView_MPI(v, viewer));
624   }
625   if (v != originalv) PetscCall(VecDestroy(&v));
626   PetscFunctionReturn(PETSC_SUCCESS);
627 }
628 
629 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
630 {
631   DM        dm;
632   PetscBool ishdf5;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
638   if (ishdf5) {
639     DM          dmBC;
640     Vec         gv;
641     const char *name;
642 
643     PetscCall(DMGetOutputDM(dm, &dmBC));
644     PetscCall(DMGetGlobalVector(dmBC, &gv));
645     PetscCall(PetscObjectGetName((PetscObject)v, &name));
646     PetscCall(PetscObjectSetName((PetscObject)gv, name));
647     PetscCall(VecLoad_Default(gv, viewer));
648     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
649     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
650     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
651   } else PetscCall(VecLoad_Default(v, viewer));
652   PetscFunctionReturn(PETSC_SUCCESS);
653 }
654 
655 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
656 {
657   DM        dm;
658   PetscBool ishdf5, isexodusii;
659 
660   PetscFunctionBegin;
661   PetscCall(VecGetDM(v, &dm));
662   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
663   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
664   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
665   if (ishdf5) {
666 #if defined(PETSC_HAVE_HDF5)
667     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
668 #else
669     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
670 #endif
671   } else if (isexodusii) {
672 #if defined(PETSC_HAVE_EXODUSII)
673     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
674 #else
675     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
676 #endif
677   } else PetscCall(VecLoad_Default(v, viewer));
678   PetscFunctionReturn(PETSC_SUCCESS);
679 }
680 
681 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
682 {
683   DM                dm;
684   PetscViewerFormat format;
685   PetscBool         ishdf5;
686 
687   PetscFunctionBegin;
688   PetscCall(VecGetDM(originalv, &dm));
689   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
690   PetscCall(PetscViewerGetFormat(viewer, &format));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
692   if (format == PETSC_VIEWER_NATIVE) {
693     if (dm->useNatural) {
694       if (dm->sfNatural) {
695         if (ishdf5) {
696 #if defined(PETSC_HAVE_HDF5)
697           Vec         v;
698           const char *vecname;
699 
700           PetscCall(DMPlexCreateNaturalVector(dm, &v));
701           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
702           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
703           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
704           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
705           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
706           PetscCall(VecDestroy(&v));
707 #else
708           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
709 #endif
710         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
711       }
712     } else PetscCall(VecLoad_Default(originalv, viewer));
713   }
714   PetscFunctionReturn(PETSC_SUCCESS);
715 }
716 
717 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
718 {
719   PetscSection       coordSection;
720   Vec                coordinates;
721   DMLabel            depthLabel, celltypeLabel;
722   const char        *name[4];
723   const PetscScalar *a;
724   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
725 
726   PetscFunctionBegin;
727   PetscCall(DMGetDimension(dm, &dim));
728   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
729   PetscCall(DMGetCoordinateSection(dm, &coordSection));
730   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
731   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
732   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
733   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
734   PetscCall(VecGetArrayRead(coordinates, &a));
735   name[0]       = "vertex";
736   name[1]       = "edge";
737   name[dim - 1] = "face";
738   name[dim]     = "cell";
739   for (c = cStart; c < cEnd; ++c) {
740     PetscInt *closure = NULL;
741     PetscInt  closureSize, cl, ct;
742 
743     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
744     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
745     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
746     PetscCall(PetscViewerASCIIPushTab(viewer));
747     for (cl = 0; cl < closureSize * 2; cl += 2) {
748       PetscInt point = closure[cl], depth, dof, off, d, p;
749 
750       if ((point < pStart) || (point >= pEnd)) continue;
751       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
752       if (!dof) continue;
753       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
754       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
755       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
756       for (p = 0; p < dof / dim; ++p) {
757         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
758         for (d = 0; d < dim; ++d) {
759           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
760           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
761         }
762         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
763       }
764       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
765     }
766     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
767     PetscCall(PetscViewerASCIIPopTab(viewer));
768   }
769   PetscCall(VecRestoreArrayRead(coordinates, &a));
770   PetscFunctionReturn(PETSC_SUCCESS);
771 }
772 
773 typedef enum {
774   CS_CARTESIAN,
775   CS_POLAR,
776   CS_CYLINDRICAL,
777   CS_SPHERICAL
778 } CoordSystem;
779 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
780 
781 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
782 {
783   PetscInt i;
784 
785   PetscFunctionBegin;
786   if (dim > 3) {
787     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
788   } else {
789     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
790 
791     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
792     switch (cs) {
793     case CS_CARTESIAN:
794       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
795       break;
796     case CS_POLAR:
797       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       break;
801     case CS_CYLINDRICAL:
802       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
803       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
804       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
805       trcoords[2] = coords[2];
806       break;
807     case CS_SPHERICAL:
808       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
809       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
810       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
811       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
812       break;
813     }
814     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
815   }
816   PetscFunctionReturn(PETSC_SUCCESS);
817 }
818 
819 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
820 {
821   DM_Plex          *mesh = (DM_Plex *)dm->data;
822   DM                cdm, cdmCell;
823   PetscSection      coordSection, coordSectionCell;
824   Vec               coordinates, coordinatesCell;
825   PetscViewerFormat format;
826 
827   PetscFunctionBegin;
828   PetscCall(PetscViewerGetFormat(viewer, &format));
829   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
830     const char *name;
831     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
832     PetscInt    pStart, pEnd, p, numLabels, l;
833     PetscMPIInt rank, size;
834 
835     PetscCall(DMGetCoordinateDM(dm, &cdm));
836     PetscCall(DMGetCoordinateSection(dm, &coordSection));
837     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
838     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
839     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
840     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
841     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
842     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
843     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
844     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
845     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
846     PetscCall(DMGetDimension(dm, &dim));
847     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
848     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
849     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
850     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
851     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
852     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
853     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
854     for (p = pStart; p < pEnd; ++p) {
855       PetscInt dof, off, s;
856 
857       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
858       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
859       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
860     }
861     PetscCall(PetscViewerFlush(viewer));
862     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
863     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
864     for (p = pStart; p < pEnd; ++p) {
865       PetscInt dof, off, c;
866 
867       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
868       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
869       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]));
870     }
871     PetscCall(PetscViewerFlush(viewer));
872     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
873     if (coordSection && coordinates) {
874       CoordSystem        cs = CS_CARTESIAN;
875       const PetscScalar *array, *arrayCell = NULL;
876       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
877       PetscMPIInt        rank;
878       const char        *name;
879 
880       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
881       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
882       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
883       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
884       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
885       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
886       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
887       pStart = PetscMin(pvStart, pcStart);
888       pEnd   = PetscMax(pvEnd, pcEnd);
889       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
890       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
891       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
892       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
893 
894       PetscCall(VecGetArrayRead(coordinates, &array));
895       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
896       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
897       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
898       for (p = pStart; p < pEnd; ++p) {
899         PetscInt dof, off;
900 
901         if (p >= pvStart && p < pvEnd) {
902           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
903           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
904           if (dof) {
905             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
906             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
907             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
908           }
909         }
910         if (cdmCell && p >= pcStart && p < pcEnd) {
911           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
912           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
913           if (dof) {
914             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
915             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
916             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
917           }
918         }
919       }
920       PetscCall(PetscViewerFlush(viewer));
921       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
922       PetscCall(VecRestoreArrayRead(coordinates, &array));
923       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
924     }
925     PetscCall(DMGetNumLabels(dm, &numLabels));
926     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
927     for (l = 0; l < numLabels; ++l) {
928       DMLabel     label;
929       PetscBool   isdepth;
930       const char *name;
931 
932       PetscCall(DMGetLabelName(dm, l, &name));
933       PetscCall(PetscStrcmp(name, "depth", &isdepth));
934       if (isdepth) continue;
935       PetscCall(DMGetLabel(dm, name, &label));
936       PetscCall(DMLabelView(label, viewer));
937     }
938     if (size > 1) {
939       PetscSF sf;
940 
941       PetscCall(DMGetPointSF(dm, &sf));
942       PetscCall(PetscSFView(sf, viewer));
943     }
944     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
945     PetscCall(PetscViewerFlush(viewer));
946   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
947     const char  *name, *color;
948     const char  *defcolors[3]  = {"gray", "orange", "green"};
949     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
950     char         lname[PETSC_MAX_PATH_LEN];
951     PetscReal    scale      = 2.0;
952     PetscReal    tikzscale  = 1.0;
953     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
954     double       tcoords[3];
955     PetscScalar *coords;
956     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
957     PetscMPIInt  rank, size;
958     char       **names, **colors, **lcolors;
959     PetscBool    flg, lflg;
960     PetscBT      wp = NULL;
961     PetscInt     pEnd, pStart;
962 
963     PetscCall(DMGetCoordinateDM(dm, &cdm));
964     PetscCall(DMGetCoordinateSection(dm, &coordSection));
965     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
966     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
967     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
968     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
969     PetscCall(DMGetDimension(dm, &dim));
970     PetscCall(DMPlexGetDepth(dm, &depth));
971     PetscCall(DMGetNumLabels(dm, &numLabels));
972     numLabels  = PetscMax(numLabels, 10);
973     numColors  = 10;
974     numLColors = 10;
975     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
976     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
977     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
978     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
979     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
980     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
981     n = 4;
982     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
983     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
984     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
985     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
986     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
987     if (!useLabels) numLabels = 0;
988     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
989     if (!useColors) {
990       numColors = 3;
991       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
992     }
993     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
994     if (!useColors) {
995       numLColors = 4;
996       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
997     }
998     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
999     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1000     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1001     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1002     if (depth < dim) plotEdges = PETSC_FALSE;
1003     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1004 
1005     /* filter points with labelvalue != labeldefaultvalue */
1006     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1007     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1008     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1009     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1010     if (lflg) {
1011       DMLabel lbl;
1012 
1013       PetscCall(DMGetLabel(dm, lname, &lbl));
1014       if (lbl) {
1015         PetscInt val, defval;
1016 
1017         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1018         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1019         for (c = pStart; c < pEnd; c++) {
1020           PetscInt *closure = NULL;
1021           PetscInt  closureSize;
1022 
1023           PetscCall(DMLabelGetValue(lbl, c, &val));
1024           if (val == defval) continue;
1025 
1026           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1027           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1028           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1029         }
1030       }
1031     }
1032 
1033     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1034     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1035     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1036     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1037 \\documentclass[tikz]{standalone}\n\n\
1038 \\usepackage{pgflibraryshapes}\n\
1039 \\usetikzlibrary{backgrounds}\n\
1040 \\usetikzlibrary{arrows}\n\
1041 \\begin{document}\n"));
1042     if (size > 1) {
1043       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1044       for (p = 0; p < size; ++p) {
1045         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1046         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1047       }
1048       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1049     }
1050     if (drawHasse) {
1051       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1052 
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1060       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1061       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1062       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1063       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1064       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1065     }
1066     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1067 
1068     /* Plot vertices */
1069     PetscCall(VecGetArray(coordinates, &coords));
1070     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1071     for (v = vStart; v < vEnd; ++v) {
1072       PetscInt  off, dof, d;
1073       PetscBool isLabeled = PETSC_FALSE;
1074 
1075       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1076       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1077       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1078       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1079       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1080       for (d = 0; d < dof; ++d) {
1081         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1082         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1083       }
1084       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1085       if (dim == 3) {
1086         PetscReal tmp = tcoords[1];
1087         tcoords[1]    = tcoords[2];
1088         tcoords[2]    = -tmp;
1089       }
1090       for (d = 0; d < dof; ++d) {
1091         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1092         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1093       }
1094       if (drawHasse) color = colors[0 % numColors];
1095       else color = colors[rank % numColors];
1096       for (l = 0; l < numLabels; ++l) {
1097         PetscInt val;
1098         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1099         if (val >= 0) {
1100           color     = lcolors[l % numLColors];
1101           isLabeled = PETSC_TRUE;
1102           break;
1103         }
1104       }
1105       if (drawNumbers[0]) {
1106         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1107       } else if (drawColors[0]) {
1108         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1109       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1110     }
1111     PetscCall(VecRestoreArray(coordinates, &coords));
1112     PetscCall(PetscViewerFlush(viewer));
1113     /* Plot edges */
1114     if (plotEdges) {
1115       PetscCall(VecGetArray(coordinates, &coords));
1116       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1117       for (e = eStart; e < eEnd; ++e) {
1118         const PetscInt *cone;
1119         PetscInt        coneSize, offA, offB, dof, d;
1120 
1121         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1122         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1123         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1124         PetscCall(DMPlexGetCone(dm, e, &cone));
1125         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1126         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1127         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1128         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1129         for (d = 0; d < dof; ++d) {
1130           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1131           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1132         }
1133         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1134         if (dim == 3) {
1135           PetscReal tmp = tcoords[1];
1136           tcoords[1]    = tcoords[2];
1137           tcoords[2]    = -tmp;
1138         }
1139         for (d = 0; d < dof; ++d) {
1140           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1141           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1142         }
1143         if (drawHasse) color = colors[1 % numColors];
1144         else color = colors[rank % numColors];
1145         for (l = 0; l < numLabels; ++l) {
1146           PetscInt val;
1147           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1148           if (val >= 0) {
1149             color = lcolors[l % numLColors];
1150             break;
1151           }
1152         }
1153         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1154       }
1155       PetscCall(VecRestoreArray(coordinates, &coords));
1156       PetscCall(PetscViewerFlush(viewer));
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1158     }
1159     /* Plot cells */
1160     if (dim == 3 || !drawNumbers[1]) {
1161       for (e = eStart; e < eEnd; ++e) {
1162         const PetscInt *cone;
1163 
1164         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1165         color = colors[rank % numColors];
1166         for (l = 0; l < numLabels; ++l) {
1167           PetscInt val;
1168           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1169           if (val >= 0) {
1170             color = lcolors[l % numLColors];
1171             break;
1172           }
1173         }
1174         PetscCall(DMPlexGetCone(dm, e, &cone));
1175         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1176       }
1177     } else {
1178       DMPolytopeType ct;
1179 
1180       /* Drawing a 2D polygon */
1181       for (c = cStart; c < cEnd; ++c) {
1182         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1183         PetscCall(DMPlexGetCellType(dm, c, &ct));
1184         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1185           const PetscInt *cone;
1186           PetscInt        coneSize, e;
1187 
1188           PetscCall(DMPlexGetCone(dm, c, &cone));
1189           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1190           for (e = 0; e < coneSize; ++e) {
1191             const PetscInt *econe;
1192 
1193             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1194             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));
1195           }
1196         } else {
1197           PetscInt *closure = NULL;
1198           PetscInt  closureSize, Nv = 0, v;
1199 
1200           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1201           for (p = 0; p < closureSize * 2; p += 2) {
1202             const PetscInt point = closure[p];
1203 
1204             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1205           }
1206           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1207           for (v = 0; v <= Nv; ++v) {
1208             const PetscInt vertex = closure[v % Nv];
1209 
1210             if (v > 0) {
1211               if (plotEdges) {
1212                 const PetscInt *edge;
1213                 PetscInt        endpoints[2], ne;
1214 
1215                 endpoints[0] = closure[v - 1];
1216                 endpoints[1] = vertex;
1217                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1218                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1219                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1220                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1221               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1222             }
1223             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1224           }
1225           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1226           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1227         }
1228       }
1229     }
1230     for (c = cStart; c < cEnd; ++c) {
1231       double             ccoords[3] = {0.0, 0.0, 0.0};
1232       PetscBool          isLabeled  = PETSC_FALSE;
1233       PetscScalar       *cellCoords = NULL;
1234       const PetscScalar *array;
1235       PetscInt           numCoords, cdim, d;
1236       PetscBool          isDG;
1237 
1238       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1239       PetscCall(DMGetCoordinateDim(dm, &cdim));
1240       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1241       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1242       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1243       for (p = 0; p < numCoords / cdim; ++p) {
1244         for (d = 0; d < cdim; ++d) {
1245           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1246           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1247         }
1248         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1249         if (cdim == 3) {
1250           PetscReal tmp = tcoords[1];
1251           tcoords[1]    = tcoords[2];
1252           tcoords[2]    = -tmp;
1253         }
1254         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1255       }
1256       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1257       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1258       for (d = 0; d < cdim; ++d) {
1259         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1260         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1261       }
1262       if (drawHasse) color = colors[depth % numColors];
1263       else color = colors[rank % numColors];
1264       for (l = 0; l < numLabels; ++l) {
1265         PetscInt val;
1266         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1267         if (val >= 0) {
1268           color     = lcolors[l % numLColors];
1269           isLabeled = PETSC_TRUE;
1270           break;
1271         }
1272       }
1273       if (drawNumbers[dim]) {
1274         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1275       } else if (drawColors[dim]) {
1276         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1277       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1278     }
1279     if (drawHasse) {
1280       color = colors[depth % numColors];
1281       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1282       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1286 
1287       color = colors[1 % numColors];
1288       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1289       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1293 
1294       color = colors[0 % numColors];
1295       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1296       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1297       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1298       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1299       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1300 
1301       for (p = pStart; p < pEnd; ++p) {
1302         const PetscInt *cone;
1303         PetscInt        coneSize, cp;
1304 
1305         PetscCall(DMPlexGetCone(dm, p, &cone));
1306         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1307         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1308       }
1309     }
1310     PetscCall(PetscViewerFlush(viewer));
1311     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1312     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1313     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1314     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1315     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1316     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1317     PetscCall(PetscFree3(names, colors, lcolors));
1318     PetscCall(PetscBTDestroy(&wp));
1319   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1320     Vec                    cown, acown;
1321     VecScatter             sct;
1322     ISLocalToGlobalMapping g2l;
1323     IS                     gid, acis;
1324     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1325     MPI_Group              ggroup, ngroup;
1326     PetscScalar           *array, nid;
1327     const PetscInt        *idxs;
1328     PetscInt              *idxs2, *start, *adjacency, *work;
1329     PetscInt64             lm[3], gm[3];
1330     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1331     PetscMPIInt            d1, d2, rank;
1332 
1333     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1334     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1335 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1336     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1337 #endif
1338     if (ncomm != MPI_COMM_NULL) {
1339       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1340       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1341       d1 = 0;
1342       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1343       nid = d2;
1344       PetscCallMPI(MPI_Group_free(&ggroup));
1345       PetscCallMPI(MPI_Group_free(&ngroup));
1346       PetscCallMPI(MPI_Comm_free(&ncomm));
1347     } else nid = 0.0;
1348 
1349     /* Get connectivity */
1350     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1351     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1352 
1353     /* filter overlapped local cells */
1354     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1355     PetscCall(ISGetIndices(gid, &idxs));
1356     PetscCall(ISGetLocalSize(gid, &cum));
1357     PetscCall(PetscMalloc1(cum, &idxs2));
1358     for (c = cStart, cum = 0; c < cEnd; c++) {
1359       if (idxs[c - cStart] < 0) continue;
1360       idxs2[cum++] = idxs[c - cStart];
1361     }
1362     PetscCall(ISRestoreIndices(gid, &idxs));
1363     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1364     PetscCall(ISDestroy(&gid));
1365     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1366 
1367     /* support for node-aware cell locality */
1368     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1369     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1370     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1371     PetscCall(VecGetArray(cown, &array));
1372     for (c = 0; c < numVertices; c++) array[c] = nid;
1373     PetscCall(VecRestoreArray(cown, &array));
1374     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1375     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1376     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1377     PetscCall(ISDestroy(&acis));
1378     PetscCall(VecScatterDestroy(&sct));
1379     PetscCall(VecDestroy(&cown));
1380 
1381     /* compute edgeCut */
1382     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1383     PetscCall(PetscMalloc1(cum, &work));
1384     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1385     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1386     PetscCall(ISDestroy(&gid));
1387     PetscCall(VecGetArray(acown, &array));
1388     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1389       PetscInt totl;
1390 
1391       totl = start[c + 1] - start[c];
1392       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1393       for (i = 0; i < totl; i++) {
1394         if (work[i] < 0) {
1395           ect += 1;
1396           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1397         }
1398       }
1399     }
1400     PetscCall(PetscFree(work));
1401     PetscCall(VecRestoreArray(acown, &array));
1402     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1403     lm[1] = -numVertices;
1404     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1405     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1406     lm[0] = ect;                     /* edgeCut */
1407     lm[1] = ectn;                    /* node-aware edgeCut */
1408     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1409     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1410     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1411 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1412     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1413 #else
1414     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1415 #endif
1416     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1417     PetscCall(PetscFree(start));
1418     PetscCall(PetscFree(adjacency));
1419     PetscCall(VecDestroy(&acown));
1420   } else {
1421     const char    *name;
1422     PetscInt      *sizes, *hybsizes, *ghostsizes;
1423     PetscInt       locDepth, depth, cellHeight, dim, d;
1424     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1425     PetscInt       numLabels, l, maxSize = 17;
1426     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1427     MPI_Comm       comm;
1428     PetscMPIInt    size, rank;
1429 
1430     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1431     PetscCallMPI(MPI_Comm_size(comm, &size));
1432     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1433     PetscCall(DMGetDimension(dm, &dim));
1434     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1435     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1436     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1437     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1438     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1439     PetscCall(DMPlexGetDepth(dm, &locDepth));
1440     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1441     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1442     gcNum = gcEnd - gcStart;
1443     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1444     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1445     for (d = 0; d <= depth; d++) {
1446       PetscInt Nc[2] = {0, 0}, ict;
1447 
1448       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1449       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1450       ict = ct0;
1451       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1452       ct0 = (DMPolytopeType)ict;
1453       for (p = pStart; p < pEnd; ++p) {
1454         DMPolytopeType ct;
1455 
1456         PetscCall(DMPlexGetCellType(dm, p, &ct));
1457         if (ct == ct0) ++Nc[0];
1458         else ++Nc[1];
1459       }
1460       if (size < maxSize) {
1461         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1462         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1463         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1464         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1465         for (p = 0; p < size; ++p) {
1466           if (rank == 0) {
1467             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1468             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1469             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1470           }
1471         }
1472       } else {
1473         PetscInt locMinMax[2];
1474 
1475         locMinMax[0] = Nc[0] + Nc[1];
1476         locMinMax[1] = Nc[0] + Nc[1];
1477         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1478         locMinMax[0] = Nc[1];
1479         locMinMax[1] = Nc[1];
1480         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1481         if (d == depth) {
1482           locMinMax[0] = gcNum;
1483           locMinMax[1] = gcNum;
1484           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1485         }
1486         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1487         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1488         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1489         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1490       }
1491       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1492     }
1493     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1494     {
1495       const PetscReal *maxCell;
1496       const PetscReal *L;
1497       PetscBool        localized;
1498 
1499       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1500       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1501       if (L || localized) {
1502         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1503         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1504         if (L) {
1505           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1506           for (d = 0; d < dim; ++d) {
1507             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1508             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1509           }
1510           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1511         }
1512         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1513         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1514       }
1515     }
1516     PetscCall(DMGetNumLabels(dm, &numLabels));
1517     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1518     for (l = 0; l < numLabels; ++l) {
1519       DMLabel         label;
1520       const char     *name;
1521       IS              valueIS;
1522       const PetscInt *values;
1523       PetscInt        numValues, v;
1524 
1525       PetscCall(DMGetLabelName(dm, l, &name));
1526       PetscCall(DMGetLabel(dm, name, &label));
1527       PetscCall(DMLabelGetNumValues(label, &numValues));
1528       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1529       PetscCall(DMLabelGetValueIS(label, &valueIS));
1530       PetscCall(ISGetIndices(valueIS, &values));
1531       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1532       for (v = 0; v < numValues; ++v) {
1533         PetscInt size;
1534 
1535         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1536         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1537         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1538       }
1539       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1540       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1541       PetscCall(ISRestoreIndices(valueIS, &values));
1542       PetscCall(ISDestroy(&valueIS));
1543     }
1544     {
1545       char    **labelNames;
1546       PetscInt  Nl = numLabels;
1547       PetscBool flg;
1548 
1549       PetscCall(PetscMalloc1(Nl, &labelNames));
1550       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1551       for (l = 0; l < Nl; ++l) {
1552         DMLabel label;
1553 
1554         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1555         if (flg) {
1556           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1557           PetscCall(DMLabelView(label, viewer));
1558         }
1559         PetscCall(PetscFree(labelNames[l]));
1560       }
1561       PetscCall(PetscFree(labelNames));
1562     }
1563     /* If no fields are specified, people do not want to see adjacency */
1564     if (dm->Nf) {
1565       PetscInt f;
1566 
1567       for (f = 0; f < dm->Nf; ++f) {
1568         const char *name;
1569 
1570         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1571         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1572         PetscCall(PetscViewerASCIIPushTab(viewer));
1573         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1574         if (dm->fields[f].adjacency[0]) {
1575           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1576           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1577         } else {
1578           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1579           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1580         }
1581         PetscCall(PetscViewerASCIIPopTab(viewer));
1582       }
1583     }
1584     PetscCall(DMGetCoarseDM(dm, &cdm));
1585     if (cdm) {
1586       PetscCall(PetscViewerASCIIPushTab(viewer));
1587       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1588       PetscCall(DMPlexView_Ascii(cdm, viewer));
1589       PetscCall(PetscViewerASCIIPopTab(viewer));
1590     }
1591   }
1592   PetscFunctionReturn(PETSC_SUCCESS);
1593 }
1594 
1595 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1596 {
1597   DMPolytopeType ct;
1598   PetscMPIInt    rank;
1599   PetscInt       cdim;
1600 
1601   PetscFunctionBegin;
1602   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1603   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1604   PetscCall(DMGetCoordinateDim(dm, &cdim));
1605   switch (ct) {
1606   case DM_POLYTOPE_SEGMENT:
1607   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1608     switch (cdim) {
1609     case 1: {
1610       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1611       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1612 
1613       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1614       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1615       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1616     } break;
1617     case 2: {
1618       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1619       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1620       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1621 
1622       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1623       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));
1624       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));
1625     } break;
1626     default:
1627       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1628     }
1629     break;
1630   case DM_POLYTOPE_TRIANGLE:
1631     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));
1632     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1633     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1635     break;
1636   case DM_POLYTOPE_QUADRILATERAL:
1637     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));
1638     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));
1639     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1640     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1641     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1642     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1643     break;
1644   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1645     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));
1646     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));
1647     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1648     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1649     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1650     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1651     break;
1652   case DM_POLYTOPE_FV_GHOST:
1653     break;
1654   default:
1655     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1656   }
1657   PetscFunctionReturn(PETSC_SUCCESS);
1658 }
1659 
1660 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1661 {
1662   DMPolytopeType ct;
1663   PetscReal      centroid[2] = {0., 0.};
1664   PetscMPIInt    rank;
1665   PetscInt       fillColor, v, e, d;
1666 
1667   PetscFunctionBegin;
1668   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1669   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1670   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1671   switch (ct) {
1672   case DM_POLYTOPE_TRIANGLE: {
1673     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1674 
1675     for (v = 0; v < 3; ++v) {
1676       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1677       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1678     }
1679     for (e = 0; e < 3; ++e) {
1680       refCoords[0] = refVertices[e * 2 + 0];
1681       refCoords[1] = refVertices[e * 2 + 1];
1682       for (d = 1; d <= edgeDiv; ++d) {
1683         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1684         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1685       }
1686       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1687       for (d = 0; d < edgeDiv; ++d) {
1688         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));
1689         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1690       }
1691     }
1692   } break;
1693   default:
1694     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1695   }
1696   PetscFunctionReturn(PETSC_SUCCESS);
1697 }
1698 
1699 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1700 {
1701   PetscDraw          draw;
1702   DM                 cdm;
1703   PetscSection       coordSection;
1704   Vec                coordinates;
1705   const PetscScalar *coords;
1706   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1707   PetscReal         *refCoords, *edgeCoords;
1708   PetscBool          isnull, drawAffine = PETSC_TRUE;
1709   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1710 
1711   PetscFunctionBegin;
1712   PetscCall(DMGetCoordinateDim(dm, &dim));
1713   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1714   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1715   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1716   PetscCall(DMGetCoordinateDM(dm, &cdm));
1717   PetscCall(DMGetLocalSection(cdm, &coordSection));
1718   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1719   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1720   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1721 
1722   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1723   PetscCall(PetscDrawIsNull(draw, &isnull));
1724   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1725   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1726 
1727   PetscCall(VecGetLocalSize(coordinates, &N));
1728   PetscCall(VecGetArrayRead(coordinates, &coords));
1729   for (c = 0; c < N; c += dim) {
1730     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1731     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1732     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1733     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1734   }
1735   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1736   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1737   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1738   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1739   PetscCall(PetscDrawClear(draw));
1740 
1741   for (c = cStart; c < cEnd; ++c) {
1742     PetscScalar *coords = NULL;
1743     PetscInt     numCoords;
1744 
1745     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1746     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1747     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1748     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1749   }
1750   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1751   PetscCall(PetscDrawFlush(draw));
1752   PetscCall(PetscDrawPause(draw));
1753   PetscCall(PetscDrawSave(draw));
1754   PetscFunctionReturn(PETSC_SUCCESS);
1755 }
1756 
1757 #if defined(PETSC_HAVE_EXODUSII)
1758   #include <exodusII.h>
1759   #include <petscviewerexodusii.h>
1760 #endif
1761 
1762 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1763 {
1764   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1765   char      name[PETSC_MAX_PATH_LEN];
1766 
1767   PetscFunctionBegin;
1768   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1769   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1770   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1771   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1772   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1773   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1774   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1775   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1776   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1777   if (iascii) {
1778     PetscViewerFormat format;
1779     PetscCall(PetscViewerGetFormat(viewer, &format));
1780     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1781     else PetscCall(DMPlexView_Ascii(dm, viewer));
1782   } else if (ishdf5) {
1783 #if defined(PETSC_HAVE_HDF5)
1784     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1785 #else
1786     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1787 #endif
1788   } else if (isvtk) {
1789     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1790   } else if (isdraw) {
1791     PetscCall(DMPlexView_Draw(dm, viewer));
1792   } else if (isglvis) {
1793     PetscCall(DMPlexView_GLVis(dm, viewer));
1794 #if defined(PETSC_HAVE_EXODUSII)
1795   } else if (isexodus) {
1796     /*
1797       exodusII requires that all sets be part of exactly one cell set.
1798       If the dm does not have a "Cell Sets" label defined, we create one
1799       with ID 1, containing all cells.
1800       Note that if the Cell Sets label is defined but does not cover all cells,
1801       we may still have a problem. This should probably be checked here or in the viewer;
1802     */
1803     PetscInt numCS;
1804     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1805     if (!numCS) {
1806       PetscInt cStart, cEnd, c;
1807       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1808       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1809       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1810     }
1811     PetscCall(DMView_PlexExodusII(dm, viewer));
1812 #endif
1813 #if defined(PETSC_HAVE_CGNS)
1814   } else if (iscgns) {
1815     PetscCall(DMView_PlexCGNS(dm, viewer));
1816 #endif
1817   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1818   /* Optionally view the partition */
1819   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1820   if (flg) {
1821     Vec ranks;
1822     PetscCall(DMPlexCreateRankField(dm, &ranks));
1823     PetscCall(VecView(ranks, viewer));
1824     PetscCall(VecDestroy(&ranks));
1825   }
1826   /* Optionally view a label */
1827   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1828   if (flg) {
1829     DMLabel label;
1830     Vec     val;
1831 
1832     PetscCall(DMGetLabel(dm, name, &label));
1833     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1834     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1835     PetscCall(VecView(val, viewer));
1836     PetscCall(VecDestroy(&val));
1837   }
1838   PetscFunctionReturn(PETSC_SUCCESS);
1839 }
1840 
1841 /*@
1842   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1843 
1844   Collective on dm
1845 
1846   Input Parameters:
1847 + dm     - The `DM` whose topology is to be saved
1848 - viewer - The `PetscViewer` to save it in
1849 
1850   Level: advanced
1851 
1852 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1853 @*/
1854 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1855 {
1856   PetscBool ishdf5;
1857 
1858   PetscFunctionBegin;
1859   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1860   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1861   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1862   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1863   if (ishdf5) {
1864 #if defined(PETSC_HAVE_HDF5)
1865     PetscViewerFormat format;
1866     PetscCall(PetscViewerGetFormat(viewer, &format));
1867     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1868       IS globalPointNumbering;
1869 
1870       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1871       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1872       PetscCall(ISDestroy(&globalPointNumbering));
1873     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1874 #else
1875     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1876 #endif
1877   }
1878   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1879   PetscFunctionReturn(PETSC_SUCCESS);
1880 }
1881 
1882 /*@
1883   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1884 
1885   Collective on dm
1886 
1887   Input Parameters:
1888 + dm     - The `DM` whose coordinates are to be saved
1889 - viewer - The `PetscViewer` for saving
1890 
1891   Level: advanced
1892 
1893 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1894 @*/
1895 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1896 {
1897   PetscBool ishdf5;
1898 
1899   PetscFunctionBegin;
1900   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1901   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1902   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1903   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1904   if (ishdf5) {
1905 #if defined(PETSC_HAVE_HDF5)
1906     PetscViewerFormat format;
1907     PetscCall(PetscViewerGetFormat(viewer, &format));
1908     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1909       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1910     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1911 #else
1912     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1913 #endif
1914   }
1915   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1916   PetscFunctionReturn(PETSC_SUCCESS);
1917 }
1918 
1919 /*@
1920   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1921 
1922   Collective on dm
1923 
1924   Input Parameters:
1925 + dm     - The `DM` whose labels are to be saved
1926 - viewer - The `PetscViewer` for saving
1927 
1928   Level: advanced
1929 
1930 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1931 @*/
1932 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1933 {
1934   PetscBool ishdf5;
1935 
1936   PetscFunctionBegin;
1937   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1938   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1939   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1940   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1941   if (ishdf5) {
1942 #if defined(PETSC_HAVE_HDF5)
1943     IS                globalPointNumbering;
1944     PetscViewerFormat format;
1945 
1946     PetscCall(PetscViewerGetFormat(viewer, &format));
1947     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1948       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1949       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1950       PetscCall(ISDestroy(&globalPointNumbering));
1951     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1952 #else
1953     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1954 #endif
1955   }
1956   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1957   PetscFunctionReturn(PETSC_SUCCESS);
1958 }
1959 
1960 /*@
1961   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1962 
1963   Collective on dm
1964 
1965   Input Parameters:
1966 + dm         - The `DM` that contains the topology on which the section to be saved is defined
1967 . viewer     - The `PetscViewer` for saving
1968 - sectiondm  - The `DM` that contains the section to be saved
1969 
1970   Level: advanced
1971 
1972   Notes:
1973   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.
1974 
1975   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.
1976 
1977 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1978 @*/
1979 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1980 {
1981   PetscBool ishdf5;
1982 
1983   PetscFunctionBegin;
1984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1985   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1986   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1987   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1988   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1989   if (ishdf5) {
1990 #if defined(PETSC_HAVE_HDF5)
1991     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1992 #else
1993     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1994 #endif
1995   }
1996   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1997   PetscFunctionReturn(PETSC_SUCCESS);
1998 }
1999 
2000 /*@
2001   DMPlexGlobalVectorView - Saves a global vector
2002 
2003   Collective on dm
2004 
2005   Input Parameters:
2006 + dm        - The `DM` that represents the topology
2007 . viewer    - The `PetscViewer` to save data with
2008 . sectiondm - The `DM` that contains the global section on which vec is defined
2009 - vec       - The global vector to be saved
2010 
2011   Level: advanced
2012 
2013   Notes:
2014   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.
2015 
2016   Typical calling sequence:
2017 .vb
2018        DMCreate(PETSC_COMM_WORLD, &dm);
2019        DMSetType(dm, DMPLEX);
2020        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2021        DMClone(dm, &sectiondm);
2022        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2023        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2024        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2025        PetscSectionSetChart(section, pStart, pEnd);
2026        PetscSectionSetUp(section);
2027        DMSetLocalSection(sectiondm, section);
2028        PetscSectionDestroy(&section);
2029        DMGetGlobalVector(sectiondm, &vec);
2030        PetscObjectSetName((PetscObject)vec, "vec_name");
2031        DMPlexTopologyView(dm, viewer);
2032        DMPlexSectionView(dm, viewer, sectiondm);
2033        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2034        DMRestoreGlobalVector(sectiondm, &vec);
2035        DMDestroy(&sectiondm);
2036        DMDestroy(&dm);
2037 .ve
2038 
2039 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2040 @*/
2041 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2042 {
2043   PetscBool ishdf5;
2044 
2045   PetscFunctionBegin;
2046   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2047   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2048   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2049   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2050   /* Check consistency */
2051   {
2052     PetscSection section;
2053     PetscBool    includesConstraints;
2054     PetscInt     m, m1;
2055 
2056     PetscCall(VecGetLocalSize(vec, &m1));
2057     PetscCall(DMGetGlobalSection(sectiondm, &section));
2058     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2059     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2060     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2061     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2062   }
2063   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2064   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2065   if (ishdf5) {
2066 #if defined(PETSC_HAVE_HDF5)
2067     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2068 #else
2069     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2070 #endif
2071   }
2072   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2073   PetscFunctionReturn(PETSC_SUCCESS);
2074 }
2075 
2076 /*@
2077   DMPlexLocalVectorView - Saves a local vector
2078 
2079   Collective on dm
2080 
2081   Input Parameters:
2082 + dm        - The `DM` that represents the topology
2083 . viewer    - The `PetscViewer` to save data with
2084 . sectiondm - The `DM` that contains the local section on which vec is defined; may be the same as dm
2085 - vec       - The local vector to be saved
2086 
2087   Level: advanced
2088 
2089   Note:
2090   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.
2091 
2092   Typical calling sequence:
2093 .vb
2094        DMCreate(PETSC_COMM_WORLD, &dm);
2095        DMSetType(dm, DMPLEX);
2096        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2097        DMClone(dm, &sectiondm);
2098        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2099        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2100        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2101        PetscSectionSetChart(section, pStart, pEnd);
2102        PetscSectionSetUp(section);
2103        DMSetLocalSection(sectiondm, section);
2104        DMGetLocalVector(sectiondm, &vec);
2105        PetscObjectSetName((PetscObject)vec, "vec_name");
2106        DMPlexTopologyView(dm, viewer);
2107        DMPlexSectionView(dm, viewer, sectiondm);
2108        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2109        DMRestoreLocalVector(sectiondm, &vec);
2110        DMDestroy(&sectiondm);
2111        DMDestroy(&dm);
2112 .ve
2113 
2114 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2115 @*/
2116 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2117 {
2118   PetscBool ishdf5;
2119 
2120   PetscFunctionBegin;
2121   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2122   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2123   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2124   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2125   /* Check consistency */
2126   {
2127     PetscSection section;
2128     PetscBool    includesConstraints;
2129     PetscInt     m, m1;
2130 
2131     PetscCall(VecGetLocalSize(vec, &m1));
2132     PetscCall(DMGetLocalSection(sectiondm, &section));
2133     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2134     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2135     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2136     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2137   }
2138   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2139   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2140   if (ishdf5) {
2141 #if defined(PETSC_HAVE_HDF5)
2142     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2143 #else
2144     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2145 #endif
2146   }
2147   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2148   PetscFunctionReturn(PETSC_SUCCESS);
2149 }
2150 
2151 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2152 {
2153   PetscBool ishdf5;
2154 
2155   PetscFunctionBegin;
2156   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2157   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2158   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2159   if (ishdf5) {
2160 #if defined(PETSC_HAVE_HDF5)
2161     PetscViewerFormat format;
2162     PetscCall(PetscViewerGetFormat(viewer, &format));
2163     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2164       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2165     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2166       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2167     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2168     PetscFunctionReturn(PETSC_SUCCESS);
2169 #else
2170     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2171 #endif
2172   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2173 }
2174 
2175 /*@
2176   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2177 
2178   Collective on dm
2179 
2180   Input Parameters:
2181 + dm                - The `DM` into which the topology is loaded
2182 - viewer            - The `PetscViewer` for the saved topology
2183 
2184   Output Parameters:
2185 . 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
2186 
2187   Level: advanced
2188 
2189 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2190           `PetscViewer`, `PetscSF`
2191 @*/
2192 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2193 {
2194   PetscBool ishdf5;
2195 
2196   PetscFunctionBegin;
2197   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2198   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2199   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2200   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2201   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2202   if (ishdf5) {
2203 #if defined(PETSC_HAVE_HDF5)
2204     PetscViewerFormat format;
2205     PetscCall(PetscViewerGetFormat(viewer, &format));
2206     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2207       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2208     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2209 #else
2210     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2211 #endif
2212   }
2213   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2214   PetscFunctionReturn(PETSC_SUCCESS);
2215 }
2216 
2217 /*@
2218   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2219 
2220   Collective on dm
2221 
2222   Input Parameters:
2223 + dm     - The `DM` into which the coordinates are loaded
2224 . viewer - The `PetscViewer` for the saved coordinates
2225 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2226 
2227   Level: advanced
2228 
2229 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2230           `PetscSF`, `PetscViewer`
2231 @*/
2232 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2233 {
2234   PetscBool ishdf5;
2235 
2236   PetscFunctionBegin;
2237   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2238   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2239   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2240   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2241   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2242   if (ishdf5) {
2243 #if defined(PETSC_HAVE_HDF5)
2244     PetscViewerFormat format;
2245     PetscCall(PetscViewerGetFormat(viewer, &format));
2246     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2247       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2248     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2249 #else
2250     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2251 #endif
2252   }
2253   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2254   PetscFunctionReturn(PETSC_SUCCESS);
2255 }
2256 
2257 /*@
2258   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2259 
2260   Collective on dm
2261 
2262   Input Parameters:
2263 + dm     - The `DM` into which the labels are loaded
2264 . viewer - The `PetscViewer` for the saved labels
2265 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2266 
2267   Level: advanced
2268 
2269   Note:
2270   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2271 
2272 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2273           `PetscSF`, `PetscViewer`
2274 @*/
2275 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2276 {
2277   PetscBool ishdf5;
2278 
2279   PetscFunctionBegin;
2280   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2281   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2282   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2283   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2284   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2285   if (ishdf5) {
2286 #if defined(PETSC_HAVE_HDF5)
2287     PetscViewerFormat format;
2288 
2289     PetscCall(PetscViewerGetFormat(viewer, &format));
2290     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2291       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2292     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2293 #else
2294     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2295 #endif
2296   }
2297   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2298   PetscFunctionReturn(PETSC_SUCCESS);
2299 }
2300 
2301 /*@
2302   DMPlexSectionLoad - Loads section into a `DMPLEX`
2303 
2304   Collective on dm
2305 
2306   Input Parameters:
2307 + dm          - The `DM` that represents the topology
2308 . viewer      - The `PetscViewer` that represents the on-disk section (sectionA)
2309 . sectiondm   - The `DM` into which the on-disk section (sectionA) is migrated
2310 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2311 
2312   Output Parameters
2313 + 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)
2314 - 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)
2315 
2316   Level: advanced
2317 
2318   Notes:
2319   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.
2320 
2321   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.
2322 
2323   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.
2324 
2325   Example using 2 processes:
2326 .vb
2327   NX (number of points on dm): 4
2328   sectionA                   : the on-disk section
2329   vecA                       : a vector associated with sectionA
2330   sectionB                   : sectiondm's local section constructed in this function
2331   vecB (local)               : a vector associated with sectiondm's local section
2332   vecB (global)              : a vector associated with sectiondm's global section
2333 
2334                                      rank 0    rank 1
2335   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2336   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2337   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2338   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2339   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2340   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2341   sectionB->atlasDof             :     1 0 1 | 1 3
2342   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2343   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2344   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2345 .ve
2346   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2347 
2348 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2349 @*/
2350 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2351 {
2352   PetscBool ishdf5;
2353 
2354   PetscFunctionBegin;
2355   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2356   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2357   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2358   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2359   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2360   if (localDofSF) PetscValidPointer(localDofSF, 6);
2361   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2362   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2363   if (ishdf5) {
2364 #if defined(PETSC_HAVE_HDF5)
2365     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2366 #else
2367     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2368 #endif
2369   }
2370   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2371   PetscFunctionReturn(PETSC_SUCCESS);
2372 }
2373 
2374 /*@
2375   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2376 
2377   Collective on dm
2378 
2379   Input Parameters:
2380 + dm        - The `DM` that represents the topology
2381 . viewer    - The `PetscViewer` that represents the on-disk vector data
2382 . sectiondm - The `DM` that contains the global section on which vec is defined
2383 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2384 - vec       - The global vector to set values of
2385 
2386   Level: advanced
2387 
2388   Notes:
2389   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.
2390 
2391   Typical calling sequence:
2392 .vb
2393        DMCreate(PETSC_COMM_WORLD, &dm);
2394        DMSetType(dm, DMPLEX);
2395        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2396        DMPlexTopologyLoad(dm, viewer, &sfX);
2397        DMClone(dm, &sectiondm);
2398        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2399        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2400        DMGetGlobalVector(sectiondm, &vec);
2401        PetscObjectSetName((PetscObject)vec, "vec_name");
2402        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2403        DMRestoreGlobalVector(sectiondm, &vec);
2404        PetscSFDestroy(&gsf);
2405        PetscSFDestroy(&sfX);
2406        DMDestroy(&sectiondm);
2407        DMDestroy(&dm);
2408 .ve
2409 
2410 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2411           `PetscSF`, `PetscViewer`
2412 @*/
2413 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2414 {
2415   PetscBool ishdf5;
2416 
2417   PetscFunctionBegin;
2418   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2419   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2420   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2421   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2422   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2423   /* Check consistency */
2424   {
2425     PetscSection section;
2426     PetscBool    includesConstraints;
2427     PetscInt     m, m1;
2428 
2429     PetscCall(VecGetLocalSize(vec, &m1));
2430     PetscCall(DMGetGlobalSection(sectiondm, &section));
2431     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2432     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2433     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2434     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2435   }
2436   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2437   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2438   if (ishdf5) {
2439 #if defined(PETSC_HAVE_HDF5)
2440     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2441 #else
2442     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2443 #endif
2444   }
2445   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2446   PetscFunctionReturn(PETSC_SUCCESS);
2447 }
2448 
2449 /*@
2450   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2451 
2452   Collective on dm
2453 
2454   Input Parameters:
2455 + dm        - The `DM` that represents the topology
2456 . viewer    - The `PetscViewer` that represents the on-disk vector data
2457 . sectiondm - The `DM` that contains the local section on which vec is defined
2458 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2459 - vec       - The local vector to set values of
2460 
2461   Level: advanced
2462 
2463   Notes:
2464   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.
2465 
2466   Typical calling sequence:
2467 .vb
2468        DMCreate(PETSC_COMM_WORLD, &dm);
2469        DMSetType(dm, DMPLEX);
2470        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2471        DMPlexTopologyLoad(dm, viewer, &sfX);
2472        DMClone(dm, &sectiondm);
2473        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2474        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2475        DMGetLocalVector(sectiondm, &vec);
2476        PetscObjectSetName((PetscObject)vec, "vec_name");
2477        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2478        DMRestoreLocalVector(sectiondm, &vec);
2479        PetscSFDestroy(&lsf);
2480        PetscSFDestroy(&sfX);
2481        DMDestroy(&sectiondm);
2482        DMDestroy(&dm);
2483 .ve
2484 
2485 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2486           `PetscSF`, `PetscViewer`
2487 @*/
2488 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2489 {
2490   PetscBool ishdf5;
2491 
2492   PetscFunctionBegin;
2493   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2494   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2495   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2496   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2497   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2498   /* Check consistency */
2499   {
2500     PetscSection section;
2501     PetscBool    includesConstraints;
2502     PetscInt     m, m1;
2503 
2504     PetscCall(VecGetLocalSize(vec, &m1));
2505     PetscCall(DMGetLocalSection(sectiondm, &section));
2506     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2507     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2508     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2509     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2510   }
2511   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2512   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2513   if (ishdf5) {
2514 #if defined(PETSC_HAVE_HDF5)
2515     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2516 #else
2517     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2518 #endif
2519   }
2520   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2521   PetscFunctionReturn(PETSC_SUCCESS);
2522 }
2523 
2524 PetscErrorCode DMDestroy_Plex(DM dm)
2525 {
2526   DM_Plex *mesh = (DM_Plex *)dm->data;
2527 
2528   PetscFunctionBegin;
2529   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2530   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2531   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2532   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2533   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2534   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2535   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2536   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2537   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2538   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2539   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2540   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2541   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2542   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2543   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2544   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2545   PetscCall(PetscFree(mesh->cones));
2546   PetscCall(PetscFree(mesh->coneOrientations));
2547   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2548   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2549   PetscCall(PetscFree(mesh->supports));
2550   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2551   PetscCall(PetscFree(mesh->facesTmp));
2552   PetscCall(PetscFree(mesh->tetgenOpts));
2553   PetscCall(PetscFree(mesh->triangleOpts));
2554   PetscCall(PetscFree(mesh->transformType));
2555   PetscCall(PetscFree(mesh->distributionName));
2556   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2557   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2558   PetscCall(ISDestroy(&mesh->subpointIS));
2559   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2560   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2561   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2562   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2563   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2564   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2565   PetscCall(ISDestroy(&mesh->anchorIS));
2566   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2567   PetscCall(PetscFree(mesh->parents));
2568   PetscCall(PetscFree(mesh->childIDs));
2569   PetscCall(PetscSectionDestroy(&mesh->childSection));
2570   PetscCall(PetscFree(mesh->children));
2571   PetscCall(DMDestroy(&mesh->referenceTree));
2572   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2573   PetscCall(PetscFree(mesh->neighbors));
2574   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2575   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2576   PetscCall(PetscFree(mesh));
2577   PetscFunctionReturn(PETSC_SUCCESS);
2578 }
2579 
2580 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2581 {
2582   PetscSection           sectionGlobal;
2583   PetscInt               bs = -1, mbs;
2584   PetscInt               localSize, localStart = 0;
2585   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2586   MatType                mtype;
2587   ISLocalToGlobalMapping ltog;
2588 
2589   PetscFunctionBegin;
2590   PetscCall(MatInitializePackage());
2591   mtype = dm->mattype;
2592   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2593   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2594   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2595   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2596   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2597   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2598   PetscCall(MatSetType(*J, mtype));
2599   PetscCall(MatSetFromOptions(*J));
2600   PetscCall(MatGetBlockSize(*J, &mbs));
2601   if (mbs > 1) bs = mbs;
2602   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2603   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2604   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2605   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2606   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2607   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2608   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2609   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2610   if (!isShell) {
2611     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2612     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2613     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2614 
2615     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2616 
2617     PetscCall(PetscCalloc1(localSize, &pblocks));
2618     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2619     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2620     for (p = pStart; p < pEnd; ++p) {
2621       switch (dm->blocking_type) {
2622       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2623         PetscInt bdof, offset;
2624 
2625         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2626         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2627         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2628         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2629         dof  = dof < 0 ? -(dof + 1) : dof;
2630         bdof = cdof && (dof - cdof) ? 1 : dof;
2631         if (dof) {
2632           if (bs < 0) {
2633             bs = bdof;
2634           } else if (bs != bdof) {
2635             bs = 1;
2636           }
2637         }
2638       } break;
2639       case DM_BLOCKING_FIELD_NODE: {
2640         for (PetscInt field = 0; field < num_fields; field++) {
2641           PetscInt num_comp, bdof, offset;
2642           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2643           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2644           if (dof < 0) continue;
2645           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2646           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2647           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);
2648           PetscInt num_nodes = dof / num_comp;
2649           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2650           // Handle possibly constant block size (unlikely)
2651           bdof = cdof && (dof - cdof) ? 1 : dof;
2652           if (dof) {
2653             if (bs < 0) {
2654               bs = bdof;
2655             } else if (bs != bdof) {
2656               bs = 1;
2657             }
2658           }
2659         }
2660       } break;
2661       }
2662     }
2663     /* Must have same blocksize on all procs (some might have no points) */
2664     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2665     bsLocal[1] = bs;
2666     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2667     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2668     else bs = bsMinMax[0];
2669     bs = PetscMax(1, bs);
2670     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2671     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2672       PetscCall(MatSetBlockSize(*J, bs));
2673       PetscCall(MatSetUp(*J));
2674     } else {
2675       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2676       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2677       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2678     }
2679     { // Consolidate blocks
2680       PetscInt nblocks = 0;
2681       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2682         if (pblocks[i] == 0) continue;
2683         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2684         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]);
2685       }
2686       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2687     }
2688     PetscCall(PetscFree(pblocks));
2689   }
2690   PetscCall(MatSetDM(*J, dm));
2691   PetscFunctionReturn(PETSC_SUCCESS);
2692 }
2693 
2694 /*@
2695   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2696 
2697   Not Collective
2698 
2699   Input Parameter:
2700 . mesh - The `DMPLEX`
2701 
2702   Output Parameters:
2703 . subsection - The subdomain section
2704 
2705   Level: developer
2706 
2707 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `PetscSection`
2708 @*/
2709 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2710 {
2711   DM_Plex *mesh = (DM_Plex *)dm->data;
2712 
2713   PetscFunctionBegin;
2714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2715   if (!mesh->subdomainSection) {
2716     PetscSection section;
2717     PetscSF      sf;
2718 
2719     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2720     PetscCall(DMGetLocalSection(dm, &section));
2721     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2722     PetscCall(PetscSFDestroy(&sf));
2723   }
2724   *subsection = mesh->subdomainSection;
2725   PetscFunctionReturn(PETSC_SUCCESS);
2726 }
2727 
2728 /*@
2729   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2730 
2731   Not Collective
2732 
2733   Input Parameter:
2734 . mesh - The `DMPLEX`
2735 
2736   Output Parameters:
2737 + pStart - The first mesh point
2738 - pEnd   - The upper bound for mesh points
2739 
2740   Level: beginner
2741 
2742 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2743 @*/
2744 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2745 {
2746   DM_Plex *mesh = (DM_Plex *)dm->data;
2747 
2748   PetscFunctionBegin;
2749   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2750   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2751   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2752   PetscFunctionReturn(PETSC_SUCCESS);
2753 }
2754 
2755 /*@
2756   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2757 
2758   Not Collective
2759 
2760   Input Parameters:
2761 + mesh - The `DMPLEX`
2762 . pStart - The first mesh point
2763 - pEnd   - The upper bound for mesh points
2764 
2765   Level: beginner
2766 
2767 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2768 @*/
2769 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2770 {
2771   DM_Plex *mesh = (DM_Plex *)dm->data;
2772 
2773   PetscFunctionBegin;
2774   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2775   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2776   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2777   PetscFunctionReturn(PETSC_SUCCESS);
2778 }
2779 
2780 /*@
2781   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2782 
2783   Not Collective
2784 
2785   Input Parameters:
2786 + mesh - The `DMPLEX`
2787 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2788 
2789   Output Parameter:
2790 . size - The cone size for point p
2791 
2792   Level: beginner
2793 
2794 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2795 @*/
2796 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2797 {
2798   DM_Plex *mesh = (DM_Plex *)dm->data;
2799 
2800   PetscFunctionBegin;
2801   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2802   PetscValidIntPointer(size, 3);
2803   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2804   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2805   PetscFunctionReturn(PETSC_SUCCESS);
2806 }
2807 
2808 /*@
2809   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2810 
2811   Not Collective
2812 
2813   Input Parameters:
2814 + mesh - The `DMPLEX`
2815 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2816 - size - The cone size for point p
2817 
2818   Level: beginner
2819 
2820   Note:
2821   This should be called after `DMPlexSetChart()`.
2822 
2823 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2824 @*/
2825 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2826 {
2827   DM_Plex *mesh = (DM_Plex *)dm->data;
2828 
2829   PetscFunctionBegin;
2830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2831   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2832   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2833   PetscFunctionReturn(PETSC_SUCCESS);
2834 }
2835 
2836 /*@C
2837   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2838 
2839   Not Collective
2840 
2841   Input Parameters:
2842 + dm - The `DMPLEX`
2843 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2844 
2845   Output Parameter:
2846 . cone - An array of points which are on the in-edges for point p
2847 
2848   Level: beginner
2849 
2850   Fortran Note:
2851   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2852   `DMPlexRestoreCone()` is not needed/available in C.
2853 
2854 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2855 @*/
2856 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2857 {
2858   DM_Plex *mesh = (DM_Plex *)dm->data;
2859   PetscInt off;
2860 
2861   PetscFunctionBegin;
2862   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2863   PetscValidPointer(cone, 3);
2864   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2865   *cone = &mesh->cones[off];
2866   PetscFunctionReturn(PETSC_SUCCESS);
2867 }
2868 
2869 /*@C
2870   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2871 
2872   Not Collective
2873 
2874   Input Parameters:
2875 + dm - The `DMPLEX`
2876 - p - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2877 
2878   Output Parameters:
2879 + pConesSection - `PetscSection` describing the layout of pCones
2880 - pCones - An array of points which are on the in-edges for the point set p
2881 
2882   Level: intermediate
2883 
2884 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2885 @*/
2886 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2887 {
2888   PetscSection cs, newcs;
2889   PetscInt    *cones;
2890   PetscInt    *newarr = NULL;
2891   PetscInt     n;
2892 
2893   PetscFunctionBegin;
2894   PetscCall(DMPlexGetCones(dm, &cones));
2895   PetscCall(DMPlexGetConeSection(dm, &cs));
2896   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2897   if (pConesSection) *pConesSection = newcs;
2898   if (pCones) {
2899     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2900     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2901   }
2902   PetscFunctionReturn(PETSC_SUCCESS);
2903 }
2904 
2905 /*@
2906   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2907 
2908   Not Collective
2909 
2910   Input Parameters:
2911 + dm - The `DMPLEX`
2912 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2913 
2914   Output Parameter:
2915 . expandedPoints - An array of vertices recursively expanded from input points
2916 
2917   Level: advanced
2918 
2919   Notes:
2920   Like `DMPlexGetConeRecursive()` but returns only the 0-depth IS (i.e. vertices only) and no sections.
2921 
2922   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2923 
2924 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2925           `DMPlexGetDepth()`, `IS`
2926 @*/
2927 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2928 {
2929   IS      *expandedPointsAll;
2930   PetscInt depth;
2931 
2932   PetscFunctionBegin;
2933   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2934   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2935   PetscValidPointer(expandedPoints, 3);
2936   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2937   *expandedPoints = expandedPointsAll[0];
2938   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2939   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2940   PetscFunctionReturn(PETSC_SUCCESS);
2941 }
2942 
2943 /*@
2944   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).
2945 
2946   Not Collective
2947 
2948   Input Parameters:
2949 + dm - The `DMPLEX`
2950 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2951 
2952   Output Parameters:
2953 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2954 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2955 - sections - (optional) An array of sections which describe mappings from points to their cone points
2956 
2957   Level: advanced
2958 
2959   Notes:
2960   Like `DMPlexGetConeTuple()` but recursive.
2961 
2962   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.
2963   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2964 
2965   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:
2966   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2967   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2968 
2969 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2970           `DMPlexGetDepth()`, `PetscSection`, `IS`
2971 @*/
2972 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2973 {
2974   const PetscInt *arr0 = NULL, *cone = NULL;
2975   PetscInt       *arr = NULL, *newarr = NULL;
2976   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2977   IS             *expandedPoints_;
2978   PetscSection   *sections_;
2979 
2980   PetscFunctionBegin;
2981   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2982   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2983   if (depth) PetscValidIntPointer(depth, 3);
2984   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2985   if (sections) PetscValidPointer(sections, 5);
2986   PetscCall(ISGetLocalSize(points, &n));
2987   PetscCall(ISGetIndices(points, &arr0));
2988   PetscCall(DMPlexGetDepth(dm, &depth_));
2989   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2990   PetscCall(PetscCalloc1(depth_, &sections_));
2991   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2992   for (d = depth_ - 1; d >= 0; d--) {
2993     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2994     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2995     for (i = 0; i < n; i++) {
2996       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2997       if (arr[i] >= start && arr[i] < end) {
2998         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2999         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3000       } else {
3001         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3002       }
3003     }
3004     PetscCall(PetscSectionSetUp(sections_[d]));
3005     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3006     PetscCall(PetscMalloc1(newn, &newarr));
3007     for (i = 0; i < n; i++) {
3008       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3009       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3010       if (cn > 1) {
3011         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3012         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3013       } else {
3014         newarr[co] = arr[i];
3015       }
3016     }
3017     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3018     arr = newarr;
3019     n   = newn;
3020   }
3021   PetscCall(ISRestoreIndices(points, &arr0));
3022   *depth = depth_;
3023   if (expandedPoints) *expandedPoints = expandedPoints_;
3024   else {
3025     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3026     PetscCall(PetscFree(expandedPoints_));
3027   }
3028   if (sections) *sections = sections_;
3029   else {
3030     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3031     PetscCall(PetscFree(sections_));
3032   }
3033   PetscFunctionReturn(PETSC_SUCCESS);
3034 }
3035 
3036 /*@
3037   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3038 
3039   Not Collective
3040 
3041   Input Parameters:
3042 + dm - The `DMPLEX`
3043 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3044 
3045   Output Parameters:
3046 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3047 . expandedPoints - (optional) An array of recursively expanded cones
3048 - sections - (optional) An array of sections which describe mappings from points to their cone points
3049 
3050   Level: advanced
3051 
3052   Note:
3053   See `DMPlexGetConeRecursive()`
3054 
3055 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3056           `DMPlexGetDepth()`, `IS`, `PetscSection`
3057 @*/
3058 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3059 {
3060   PetscInt d, depth_;
3061 
3062   PetscFunctionBegin;
3063   PetscCall(DMPlexGetDepth(dm, &depth_));
3064   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3065   if (depth) *depth = 0;
3066   if (expandedPoints) {
3067     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3068     PetscCall(PetscFree(*expandedPoints));
3069   }
3070   if (sections) {
3071     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3072     PetscCall(PetscFree(*sections));
3073   }
3074   PetscFunctionReturn(PETSC_SUCCESS);
3075 }
3076 
3077 /*@
3078   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
3079 
3080   Not Collective
3081 
3082   Input Parameters:
3083 + mesh - The `DMPLEX`
3084 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3085 - cone - An array of points which are on the in-edges for point p
3086 
3087   Level: beginner
3088 
3089   Note:
3090   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3091 
3092 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3093 @*/
3094 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3095 {
3096   DM_Plex *mesh = (DM_Plex *)dm->data;
3097   PetscInt pStart, pEnd;
3098   PetscInt dof, off, c;
3099 
3100   PetscFunctionBegin;
3101   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3102   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3103   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3104   if (dof) PetscValidIntPointer(cone, 3);
3105   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3106   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);
3107   for (c = 0; c < dof; ++c) {
3108     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);
3109     mesh->cones[off + c] = cone[c];
3110   }
3111   PetscFunctionReturn(PETSC_SUCCESS);
3112 }
3113 
3114 /*@C
3115   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3116 
3117   Not Collective
3118 
3119   Input Parameters:
3120 + mesh - The `DMPLEX`
3121 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3122 
3123   Output Parameter:
3124 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3125                     integer giving the prescription for cone traversal.
3126 
3127   Level: beginner
3128 
3129   Note:
3130   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3131   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3132   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3133   with the identity.
3134 
3135   Fortran Note:
3136   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3137   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3138 
3139 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3140 @*/
3141 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3142 {
3143   DM_Plex *mesh = (DM_Plex *)dm->data;
3144   PetscInt off;
3145 
3146   PetscFunctionBegin;
3147   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3148   if (PetscDefined(USE_DEBUG)) {
3149     PetscInt dof;
3150     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3151     if (dof) PetscValidPointer(coneOrientation, 3);
3152   }
3153   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3154 
3155   *coneOrientation = &mesh->coneOrientations[off];
3156   PetscFunctionReturn(PETSC_SUCCESS);
3157 }
3158 
3159 /*@
3160   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3161 
3162   Not Collective
3163 
3164   Input Parameters:
3165 + mesh - The `DMPLEX`
3166 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3167 - coneOrientation - An array of orientations
3168 
3169   Level: beginner
3170 
3171   Notes:
3172   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3173 
3174   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3175 
3176 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3177 @*/
3178 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3179 {
3180   DM_Plex *mesh = (DM_Plex *)dm->data;
3181   PetscInt pStart, pEnd;
3182   PetscInt dof, off, c;
3183 
3184   PetscFunctionBegin;
3185   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3186   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3187   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3188   if (dof) PetscValidIntPointer(coneOrientation, 3);
3189   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3190   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);
3191   for (c = 0; c < dof; ++c) {
3192     PetscInt cdof, o = coneOrientation[c];
3193 
3194     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3195     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);
3196     mesh->coneOrientations[off + c] = o;
3197   }
3198   PetscFunctionReturn(PETSC_SUCCESS);
3199 }
3200 
3201 /*@
3202   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3203 
3204   Not Collective
3205 
3206   Input Parameters:
3207 + mesh - The `DMPLEX`
3208 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3209 . conePos - The local index in the cone where the point should be put
3210 - conePoint - The mesh point to insert
3211 
3212   Level: beginner
3213 
3214 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3215 @*/
3216 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3217 {
3218   DM_Plex *mesh = (DM_Plex *)dm->data;
3219   PetscInt pStart, pEnd;
3220   PetscInt dof, off;
3221 
3222   PetscFunctionBegin;
3223   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3224   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3225   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);
3226   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);
3227   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3228   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3229   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);
3230   mesh->cones[off + conePos] = conePoint;
3231   PetscFunctionReturn(PETSC_SUCCESS);
3232 }
3233 
3234 /*@
3235   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3236 
3237   Not Collective
3238 
3239   Input Parameters:
3240 + mesh - The `DMPLEX`
3241 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3242 . conePos - The local index in the cone where the point should be put
3243 - coneOrientation - The point orientation to insert
3244 
3245   Level: beginner
3246 
3247   Note:
3248   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3249 
3250 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3251 @*/
3252 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3253 {
3254   DM_Plex *mesh = (DM_Plex *)dm->data;
3255   PetscInt pStart, pEnd;
3256   PetscInt dof, off;
3257 
3258   PetscFunctionBegin;
3259   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3260   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3261   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3262   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3263   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3264   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);
3265   mesh->coneOrientations[off + conePos] = coneOrientation;
3266   PetscFunctionReturn(PETSC_SUCCESS);
3267 }
3268 
3269 /*@C
3270   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3271 
3272   Not collective
3273 
3274   Input Parameters:
3275 + dm - The DMPlex
3276 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3277 
3278   Output Parameters:
3279 + cone - An array of points which are on the in-edges for point p
3280 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3281         integer giving the prescription for cone traversal.
3282 
3283   Level: beginner
3284 
3285   Notes:
3286   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3287   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3288   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3289   with the identity.
3290 
3291   Fortran Notes:
3292   Since it returns an array, this routine is only available in Fortran 90, and you must
3293   include petsc.h90 in your code.
3294   You must also call DMPlexRestoreCone() after you finish using the returned array.
3295   DMPlexRestoreCone() is not needed/available in C.
3296 
3297 .seealso: `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3298 @*/
3299 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3300 {
3301   DM_Plex *mesh = (DM_Plex *)dm->data;
3302 
3303   PetscFunctionBegin;
3304   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3305   if (mesh->tr) {
3306     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3307   } else {
3308     PetscInt off;
3309     if (PetscDefined(USE_DEBUG)) {
3310       PetscInt dof;
3311       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3312       if (dof) {
3313         if (cone) PetscValidPointer(cone, 3);
3314         if (ornt) PetscValidPointer(ornt, 4);
3315       }
3316     }
3317     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3318     if (cone) *cone = &mesh->cones[off];
3319     if (ornt) *ornt = &mesh->coneOrientations[off];
3320   }
3321   PetscFunctionReturn(PETSC_SUCCESS);
3322 }
3323 
3324 /*@C
3325   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3326 
3327   Not collective
3328 
3329   Input Parameters:
3330 + dm - The DMPlex
3331 . p  - The point, which must lie in the chart set with DMPlexSetChart()
3332 . cone - An array of points which are on the in-edges for point p
3333 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3334         integer giving the prescription for cone traversal.
3335 
3336   Level: beginner
3337 
3338   Notes:
3339   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3340   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3341   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3342   with the identity.
3343 
3344   Fortran Notes:
3345   Since it returns an array, this routine is only available in Fortran 90, and you must
3346   include petsc.h90 in your code.
3347   You must also call DMPlexRestoreCone() after you finish using the returned array.
3348   DMPlexRestoreCone() is not needed/available in C.
3349 
3350 .seealso: `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3351 @*/
3352 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3353 {
3354   DM_Plex *mesh = (DM_Plex *)dm->data;
3355 
3356   PetscFunctionBegin;
3357   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3358   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3359   PetscFunctionReturn(PETSC_SUCCESS);
3360 }
3361 
3362 /*@
3363   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3364 
3365   Not Collective
3366 
3367   Input Parameters:
3368 + mesh - The `DMPLEX`
3369 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3370 
3371   Output Parameter:
3372 . size - The support size for point p
3373 
3374   Level: beginner
3375 
3376 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3377 @*/
3378 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3379 {
3380   DM_Plex *mesh = (DM_Plex *)dm->data;
3381 
3382   PetscFunctionBegin;
3383   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3384   PetscValidIntPointer(size, 3);
3385   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3386   PetscFunctionReturn(PETSC_SUCCESS);
3387 }
3388 
3389 /*@
3390   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3391 
3392   Not Collective
3393 
3394   Input Parameters:
3395 + mesh - The `DMPLEX`
3396 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3397 - size - The support size for point p
3398 
3399   Level: beginner
3400 
3401   Note:
3402   This should be called after DMPlexSetChart().
3403 
3404 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3405 @*/
3406 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3407 {
3408   DM_Plex *mesh = (DM_Plex *)dm->data;
3409 
3410   PetscFunctionBegin;
3411   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3412   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3413   PetscFunctionReturn(PETSC_SUCCESS);
3414 }
3415 
3416 /*@C
3417   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3418 
3419   Not Collective
3420 
3421   Input Parameters:
3422 + mesh - The `DMPLEX`
3423 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3424 
3425   Output Parameter:
3426 . support - An array of points which are on the out-edges for point p
3427 
3428   Level: beginner
3429 
3430   Fortran Note:
3431   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3432   `DMPlexRestoreSupport()` is not needed/available in C.
3433 
3434 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3435 @*/
3436 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3437 {
3438   DM_Plex *mesh = (DM_Plex *)dm->data;
3439   PetscInt off;
3440 
3441   PetscFunctionBegin;
3442   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3443   PetscValidPointer(support, 3);
3444   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3445   *support = &mesh->supports[off];
3446   PetscFunctionReturn(PETSC_SUCCESS);
3447 }
3448 
3449 /*@
3450   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3451 
3452   Not Collective
3453 
3454   Input Parameters:
3455 + mesh - The `DMPLEX`
3456 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3457 - support - An array of points which are on the out-edges for point p
3458 
3459   Level: beginner
3460 
3461   Note:
3462   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3463 
3464 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3465 @*/
3466 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3467 {
3468   DM_Plex *mesh = (DM_Plex *)dm->data;
3469   PetscInt pStart, pEnd;
3470   PetscInt dof, off, c;
3471 
3472   PetscFunctionBegin;
3473   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3474   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3475   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3476   if (dof) PetscValidIntPointer(support, 3);
3477   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3478   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);
3479   for (c = 0; c < dof; ++c) {
3480     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);
3481     mesh->supports[off + c] = support[c];
3482   }
3483   PetscFunctionReturn(PETSC_SUCCESS);
3484 }
3485 
3486 /*@
3487   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3488 
3489   Not Collective
3490 
3491   Input Parameters:
3492 + mesh - The `DMPLEX`
3493 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3494 . supportPos - The local index in the cone where the point should be put
3495 - supportPoint - The mesh point to insert
3496 
3497   Level: beginner
3498 
3499 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3500 @*/
3501 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3502 {
3503   DM_Plex *mesh = (DM_Plex *)dm->data;
3504   PetscInt pStart, pEnd;
3505   PetscInt dof, off;
3506 
3507   PetscFunctionBegin;
3508   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3509   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3510   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3511   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3512   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);
3513   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);
3514   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);
3515   mesh->supports[off + supportPos] = supportPoint;
3516   PetscFunctionReturn(PETSC_SUCCESS);
3517 }
3518 
3519 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3520 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3521 {
3522   switch (ct) {
3523   case DM_POLYTOPE_SEGMENT:
3524     if (o == -1) return -2;
3525     break;
3526   case DM_POLYTOPE_TRIANGLE:
3527     if (o == -3) return -1;
3528     if (o == -2) return -3;
3529     if (o == -1) return -2;
3530     break;
3531   case DM_POLYTOPE_QUADRILATERAL:
3532     if (o == -4) return -2;
3533     if (o == -3) return -1;
3534     if (o == -2) return -4;
3535     if (o == -1) return -3;
3536     break;
3537   default:
3538     return o;
3539   }
3540   return o;
3541 }
3542 
3543 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3544 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3545 {
3546   switch (ct) {
3547   case DM_POLYTOPE_SEGMENT:
3548     if ((o == -2) || (o == 1)) return -1;
3549     if (o == -1) return 0;
3550     break;
3551   case DM_POLYTOPE_TRIANGLE:
3552     if (o == -3) return -2;
3553     if (o == -2) return -1;
3554     if (o == -1) return -3;
3555     break;
3556   case DM_POLYTOPE_QUADRILATERAL:
3557     if (o == -4) return -2;
3558     if (o == -3) return -1;
3559     if (o == -2) return -4;
3560     if (o == -1) return -3;
3561     break;
3562   default:
3563     return o;
3564   }
3565   return o;
3566 }
3567 
3568 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3569 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3570 {
3571   PetscInt pStart, pEnd, p;
3572 
3573   PetscFunctionBegin;
3574   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3575   for (p = pStart; p < pEnd; ++p) {
3576     const PetscInt *cone, *ornt;
3577     PetscInt        coneSize, c;
3578 
3579     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3580     PetscCall(DMPlexGetCone(dm, p, &cone));
3581     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3582     for (c = 0; c < coneSize; ++c) {
3583       DMPolytopeType ct;
3584       const PetscInt o = ornt[c];
3585 
3586       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3587       switch (ct) {
3588       case DM_POLYTOPE_SEGMENT:
3589         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3590         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3591         break;
3592       case DM_POLYTOPE_TRIANGLE:
3593         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3594         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3595         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3596         break;
3597       case DM_POLYTOPE_QUADRILATERAL:
3598         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3599         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3600         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3601         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3602         break;
3603       default:
3604         break;
3605       }
3606     }
3607   }
3608   PetscFunctionReturn(PETSC_SUCCESS);
3609 }
3610 
3611 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3612 {
3613   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3614   PetscInt       *closure;
3615   const PetscInt *tmp = NULL, *tmpO = NULL;
3616   PetscInt        off = 0, tmpSize, t;
3617 
3618   PetscFunctionBeginHot;
3619   if (ornt) {
3620     PetscCall(DMPlexGetCellType(dm, p, &ct));
3621     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3622   }
3623   if (*points) {
3624     closure = *points;
3625   } else {
3626     PetscInt maxConeSize, maxSupportSize;
3627     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3628     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3629   }
3630   if (useCone) {
3631     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3632     PetscCall(DMPlexGetCone(dm, p, &tmp));
3633     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3634   } else {
3635     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3636     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3637   }
3638   if (ct == DM_POLYTOPE_UNKNOWN) {
3639     closure[off++] = p;
3640     closure[off++] = 0;
3641     for (t = 0; t < tmpSize; ++t) {
3642       closure[off++] = tmp[t];
3643       closure[off++] = tmpO ? tmpO[t] : 0;
3644     }
3645   } else {
3646     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3647 
3648     /* We assume that cells with a valid type have faces with a valid type */
3649     closure[off++] = p;
3650     closure[off++] = ornt;
3651     for (t = 0; t < tmpSize; ++t) {
3652       DMPolytopeType ft;
3653 
3654       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3655       closure[off++] = tmp[arr[t]];
3656       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3657     }
3658   }
3659   if (numPoints) *numPoints = tmpSize + 1;
3660   if (points) *points = closure;
3661   PetscFunctionReturn(PETSC_SUCCESS);
3662 }
3663 
3664 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3665 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3666 {
3667   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3668   const PetscInt *cone, *ornt;
3669   PetscInt       *pts, *closure = NULL;
3670   DMPolytopeType  ft;
3671   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3672   PetscInt        dim, coneSize, c, d, clSize, cl;
3673 
3674   PetscFunctionBeginHot;
3675   PetscCall(DMGetDimension(dm, &dim));
3676   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3677   PetscCall(DMPlexGetCone(dm, point, &cone));
3678   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3679   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3680   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3681   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3682   maxSize       = PetscMax(coneSeries, supportSeries);
3683   if (*points) {
3684     pts = *points;
3685   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3686   c        = 0;
3687   pts[c++] = point;
3688   pts[c++] = o;
3689   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3690   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3691   for (cl = 0; cl < clSize * 2; cl += 2) {
3692     pts[c++] = closure[cl];
3693     pts[c++] = closure[cl + 1];
3694   }
3695   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3696   for (cl = 0; cl < clSize * 2; cl += 2) {
3697     pts[c++] = closure[cl];
3698     pts[c++] = closure[cl + 1];
3699   }
3700   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3701   for (d = 2; d < coneSize; ++d) {
3702     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3703     pts[c++] = cone[arr[d * 2 + 0]];
3704     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3705   }
3706   if (dim >= 3) {
3707     for (d = 2; d < coneSize; ++d) {
3708       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3709       const PetscInt *fcone, *fornt;
3710       PetscInt        fconeSize, fc, i;
3711 
3712       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3713       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3714       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3715       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3716       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3717       for (fc = 0; fc < fconeSize; ++fc) {
3718         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3719         const PetscInt co = farr[fc * 2 + 1];
3720 
3721         for (i = 0; i < c; i += 2)
3722           if (pts[i] == cp) break;
3723         if (i == c) {
3724           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3725           pts[c++] = cp;
3726           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3727         }
3728       }
3729     }
3730   }
3731   *numPoints = c / 2;
3732   *points    = pts;
3733   PetscFunctionReturn(PETSC_SUCCESS);
3734 }
3735 
3736 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3737 {
3738   DMPolytopeType ct;
3739   PetscInt      *closure, *fifo;
3740   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3741   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3742   PetscInt       depth, maxSize;
3743 
3744   PetscFunctionBeginHot;
3745   PetscCall(DMPlexGetDepth(dm, &depth));
3746   if (depth == 1) {
3747     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3748     PetscFunctionReturn(PETSC_SUCCESS);
3749   }
3750   PetscCall(DMPlexGetCellType(dm, p, &ct));
3751   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3752   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3753     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3754     PetscFunctionReturn(PETSC_SUCCESS);
3755   }
3756   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3757   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3758   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3759   maxSize       = PetscMax(coneSeries, supportSeries);
3760   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3761   if (*points) {
3762     closure = *points;
3763   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3764   closure[closureSize++] = p;
3765   closure[closureSize++] = ornt;
3766   fifo[fifoSize++]       = p;
3767   fifo[fifoSize++]       = ornt;
3768   fifo[fifoSize++]       = ct;
3769   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3770   while (fifoSize - fifoStart) {
3771     const PetscInt       q    = fifo[fifoStart++];
3772     const PetscInt       o    = fifo[fifoStart++];
3773     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3774     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3775     const PetscInt      *tmp, *tmpO;
3776     PetscInt             tmpSize, t;
3777 
3778     if (PetscDefined(USE_DEBUG)) {
3779       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3780       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);
3781     }
3782     if (useCone) {
3783       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3784       PetscCall(DMPlexGetCone(dm, q, &tmp));
3785       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3786     } else {
3787       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3788       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3789       tmpO = NULL;
3790     }
3791     for (t = 0; t < tmpSize; ++t) {
3792       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3793       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3794       const PetscInt cp = tmp[ip];
3795       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3796       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3797       PetscInt       c;
3798 
3799       /* Check for duplicate */
3800       for (c = 0; c < closureSize; c += 2) {
3801         if (closure[c] == cp) break;
3802       }
3803       if (c == closureSize) {
3804         closure[closureSize++] = cp;
3805         closure[closureSize++] = co;
3806         fifo[fifoSize++]       = cp;
3807         fifo[fifoSize++]       = co;
3808         fifo[fifoSize++]       = ct;
3809       }
3810     }
3811   }
3812   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3813   if (numPoints) *numPoints = closureSize / 2;
3814   if (points) *points = closure;
3815   PetscFunctionReturn(PETSC_SUCCESS);
3816 }
3817 
3818 /*@C
3819   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3820 
3821   Not Collective
3822 
3823   Input Parameters:
3824 + dm      - The `DMPLEX`
3825 . p       - The mesh point
3826 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3827 
3828   Input/Output Parameter:
3829 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3830            if NULL on input, internal storage will be returned, otherwise the provided array is used
3831 
3832   Output Parameter:
3833 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3834 
3835   Level: beginner
3836 
3837   Note:
3838   If using internal storage (points is NULL on input), each call overwrites the last output.
3839 
3840   Fortran Note:
3841   The numPoints argument is not present in the Fortran binding since it is internal to the array.
3842 
3843 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3844 @*/
3845 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3846 {
3847   PetscFunctionBeginHot;
3848   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3849   if (numPoints) PetscValidIntPointer(numPoints, 4);
3850   if (points) PetscValidPointer(points, 5);
3851   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3852   PetscFunctionReturn(PETSC_SUCCESS);
3853 }
3854 
3855 /*@C
3856   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3857 
3858   Not Collective
3859 
3860   Input Parameters:
3861 + dm        - The `DMPLEX`
3862 . p         - The mesh point
3863 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3864 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3865 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3866 
3867   Level: beginner
3868 
3869   Note:
3870   If not using internal storage (points is not NULL on input), this call is unnecessary
3871 
3872 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3873 @*/
3874 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3875 {
3876   PetscFunctionBeginHot;
3877   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3878   if (numPoints) *numPoints = 0;
3879   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3880   PetscFunctionReturn(PETSC_SUCCESS);
3881 }
3882 
3883 /*@
3884   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3885 
3886   Not Collective
3887 
3888   Input Parameter:
3889 . mesh - The `DMPLEX`
3890 
3891   Output Parameters:
3892 + maxConeSize - The maximum number of in-edges
3893 - maxSupportSize - The maximum number of out-edges
3894 
3895   Level: beginner
3896 
3897 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3898 @*/
3899 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3900 {
3901   DM_Plex *mesh = (DM_Plex *)dm->data;
3902 
3903   PetscFunctionBegin;
3904   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3905   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3906   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3907   PetscFunctionReturn(PETSC_SUCCESS);
3908 }
3909 
3910 PetscErrorCode DMSetUp_Plex(DM dm)
3911 {
3912   DM_Plex *mesh = (DM_Plex *)dm->data;
3913   PetscInt size, maxSupportSize;
3914 
3915   PetscFunctionBegin;
3916   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3917   PetscCall(PetscSectionSetUp(mesh->coneSection));
3918   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3919   PetscCall(PetscMalloc1(size, &mesh->cones));
3920   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3921   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3922   if (maxSupportSize) {
3923     PetscCall(PetscSectionSetUp(mesh->supportSection));
3924     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3925     PetscCall(PetscMalloc1(size, &mesh->supports));
3926   }
3927   PetscFunctionReturn(PETSC_SUCCESS);
3928 }
3929 
3930 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3931 {
3932   PetscFunctionBegin;
3933   if (subdm) PetscCall(DMClone(dm, subdm));
3934   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3935   if (subdm) (*subdm)->useNatural = dm->useNatural;
3936   if (dm->useNatural && dm->sfMigration) {
3937     PetscSF sfNatural;
3938 
3939     (*subdm)->sfMigration = dm->sfMigration;
3940     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3941     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3942     (*subdm)->sfNatural = sfNatural;
3943   }
3944   PetscFunctionReturn(PETSC_SUCCESS);
3945 }
3946 
3947 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3948 {
3949   PetscInt i = 0;
3950 
3951   PetscFunctionBegin;
3952   PetscCall(DMClone(dms[0], superdm));
3953   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3954   (*superdm)->useNatural = PETSC_FALSE;
3955   for (i = 0; i < len; i++) {
3956     if (dms[i]->useNatural && dms[i]->sfMigration) {
3957       PetscSF sfNatural;
3958 
3959       (*superdm)->sfMigration = dms[i]->sfMigration;
3960       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3961       (*superdm)->useNatural = PETSC_TRUE;
3962       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3963       (*superdm)->sfNatural = sfNatural;
3964       break;
3965     }
3966   }
3967   PetscFunctionReturn(PETSC_SUCCESS);
3968 }
3969 
3970 /*@
3971   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3972 
3973   Not Collective
3974 
3975   Input Parameter:
3976 . mesh - The `DMPLEX`
3977 
3978   Level: beginner
3979 
3980   Note:
3981   This should be called after all calls to `DMPlexSetCone()`
3982 
3983 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3984 @*/
3985 PetscErrorCode DMPlexSymmetrize(DM dm)
3986 {
3987   DM_Plex  *mesh = (DM_Plex *)dm->data;
3988   PetscInt *offsets;
3989   PetscInt  supportSize;
3990   PetscInt  pStart, pEnd, p;
3991 
3992   PetscFunctionBegin;
3993   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3994   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3995   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3996   /* Calculate support sizes */
3997   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3998   for (p = pStart; p < pEnd; ++p) {
3999     PetscInt dof, off, c;
4000 
4001     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4002     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4003     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4004   }
4005   PetscCall(PetscSectionSetUp(mesh->supportSection));
4006   /* Calculate supports */
4007   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4008   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4009   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4010   for (p = pStart; p < pEnd; ++p) {
4011     PetscInt dof, off, c;
4012 
4013     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4014     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4015     for (c = off; c < off + dof; ++c) {
4016       const PetscInt q = mesh->cones[c];
4017       PetscInt       offS;
4018 
4019       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4020 
4021       mesh->supports[offS + offsets[q]] = p;
4022       ++offsets[q];
4023     }
4024   }
4025   PetscCall(PetscFree(offsets));
4026   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4027   PetscFunctionReturn(PETSC_SUCCESS);
4028 }
4029 
4030 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4031 {
4032   IS stratumIS;
4033 
4034   PetscFunctionBegin;
4035   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4036   if (PetscDefined(USE_DEBUG)) {
4037     PetscInt  qStart, qEnd, numLevels, level;
4038     PetscBool overlap = PETSC_FALSE;
4039     PetscCall(DMLabelGetNumValues(label, &numLevels));
4040     for (level = 0; level < numLevels; level++) {
4041       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4042       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4043         overlap = PETSC_TRUE;
4044         break;
4045       }
4046     }
4047     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);
4048   }
4049   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4050   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4051   PetscCall(ISDestroy(&stratumIS));
4052   PetscFunctionReturn(PETSC_SUCCESS);
4053 }
4054 
4055 /*@
4056   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4057   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4058   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4059   the DAG.
4060 
4061   Collective on dm
4062 
4063   Input Parameter:
4064 . mesh - The `DMPLEX`
4065 
4066   Level: beginner
4067 
4068   Notes:
4069   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4070   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4071   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4072   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4073   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4074 
4075   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4076   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4077   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
4078   to interpolate only that one (e0), so that
4079 .vb
4080   cone(c0) = {e0, v2}
4081   cone(e0) = {v0, v1}
4082 .ve
4083   If `DMPlexStratify()` is run on this mesh, it will give depths
4084 .vb
4085    depth 0 = {v0, v1, v2}
4086    depth 1 = {e0, c0}
4087 .ve
4088   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4089 
4090   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4091 
4092 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4093 @*/
4094 PetscErrorCode DMPlexStratify(DM dm)
4095 {
4096   DM_Plex *mesh = (DM_Plex *)dm->data;
4097   DMLabel  label;
4098   PetscInt pStart, pEnd, p;
4099   PetscInt numRoots = 0, numLeaves = 0;
4100 
4101   PetscFunctionBegin;
4102   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4103   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4104 
4105   /* Create depth label */
4106   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4107   PetscCall(DMCreateLabel(dm, "depth"));
4108   PetscCall(DMPlexGetDepthLabel(dm, &label));
4109 
4110   {
4111     /* Initialize roots and count leaves */
4112     PetscInt sMin = PETSC_MAX_INT;
4113     PetscInt sMax = PETSC_MIN_INT;
4114     PetscInt coneSize, supportSize;
4115 
4116     for (p = pStart; p < pEnd; ++p) {
4117       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4118       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4119       if (!coneSize && supportSize) {
4120         sMin = PetscMin(p, sMin);
4121         sMax = PetscMax(p, sMax);
4122         ++numRoots;
4123       } else if (!supportSize && coneSize) {
4124         ++numLeaves;
4125       } else if (!supportSize && !coneSize) {
4126         /* Isolated points */
4127         sMin = PetscMin(p, sMin);
4128         sMax = PetscMax(p, sMax);
4129       }
4130     }
4131     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4132   }
4133 
4134   if (numRoots + numLeaves == (pEnd - pStart)) {
4135     PetscInt sMin = PETSC_MAX_INT;
4136     PetscInt sMax = PETSC_MIN_INT;
4137     PetscInt coneSize, supportSize;
4138 
4139     for (p = pStart; p < pEnd; ++p) {
4140       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4141       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4142       if (!supportSize && coneSize) {
4143         sMin = PetscMin(p, sMin);
4144         sMax = PetscMax(p, sMax);
4145       }
4146     }
4147     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4148   } else {
4149     PetscInt level = 0;
4150     PetscInt qStart, qEnd, q;
4151 
4152     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4153     while (qEnd > qStart) {
4154       PetscInt sMin = PETSC_MAX_INT;
4155       PetscInt sMax = PETSC_MIN_INT;
4156 
4157       for (q = qStart; q < qEnd; ++q) {
4158         const PetscInt *support;
4159         PetscInt        supportSize, s;
4160 
4161         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4162         PetscCall(DMPlexGetSupport(dm, q, &support));
4163         for (s = 0; s < supportSize; ++s) {
4164           sMin = PetscMin(support[s], sMin);
4165           sMax = PetscMax(support[s], sMax);
4166         }
4167       }
4168       PetscCall(DMLabelGetNumValues(label, &level));
4169       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4170       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4171     }
4172   }
4173   { /* just in case there is an empty process */
4174     PetscInt numValues, maxValues = 0, v;
4175 
4176     PetscCall(DMLabelGetNumValues(label, &numValues));
4177     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4178     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4179   }
4180   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4181   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4182   PetscFunctionReturn(PETSC_SUCCESS);
4183 }
4184 
4185 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4186 {
4187   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4188   PetscInt       dim, depth, pheight, coneSize;
4189 
4190   PetscFunctionBeginHot;
4191   PetscCall(DMGetDimension(dm, &dim));
4192   PetscCall(DMPlexGetDepth(dm, &depth));
4193   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4194   pheight = depth - pdepth;
4195   if (depth <= 1) {
4196     switch (pdepth) {
4197     case 0:
4198       ct = DM_POLYTOPE_POINT;
4199       break;
4200     case 1:
4201       switch (coneSize) {
4202       case 2:
4203         ct = DM_POLYTOPE_SEGMENT;
4204         break;
4205       case 3:
4206         ct = DM_POLYTOPE_TRIANGLE;
4207         break;
4208       case 4:
4209         switch (dim) {
4210         case 2:
4211           ct = DM_POLYTOPE_QUADRILATERAL;
4212           break;
4213         case 3:
4214           ct = DM_POLYTOPE_TETRAHEDRON;
4215           break;
4216         default:
4217           break;
4218         }
4219         break;
4220       case 5:
4221         ct = DM_POLYTOPE_PYRAMID;
4222         break;
4223       case 6:
4224         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4225         break;
4226       case 8:
4227         ct = DM_POLYTOPE_HEXAHEDRON;
4228         break;
4229       default:
4230         break;
4231       }
4232     }
4233   } else {
4234     if (pdepth == 0) {
4235       ct = DM_POLYTOPE_POINT;
4236     } else if (pheight == 0) {
4237       switch (dim) {
4238       case 1:
4239         switch (coneSize) {
4240         case 2:
4241           ct = DM_POLYTOPE_SEGMENT;
4242           break;
4243         default:
4244           break;
4245         }
4246         break;
4247       case 2:
4248         switch (coneSize) {
4249         case 3:
4250           ct = DM_POLYTOPE_TRIANGLE;
4251           break;
4252         case 4:
4253           ct = DM_POLYTOPE_QUADRILATERAL;
4254           break;
4255         default:
4256           break;
4257         }
4258         break;
4259       case 3:
4260         switch (coneSize) {
4261         case 4:
4262           ct = DM_POLYTOPE_TETRAHEDRON;
4263           break;
4264         case 5: {
4265           const PetscInt *cone;
4266           PetscInt        faceConeSize;
4267 
4268           PetscCall(DMPlexGetCone(dm, p, &cone));
4269           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4270           switch (faceConeSize) {
4271           case 3:
4272             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4273             break;
4274           case 4:
4275             ct = DM_POLYTOPE_PYRAMID;
4276             break;
4277           }
4278         } break;
4279         case 6:
4280           ct = DM_POLYTOPE_HEXAHEDRON;
4281           break;
4282         default:
4283           break;
4284         }
4285         break;
4286       default:
4287         break;
4288       }
4289     } else if (pheight > 0) {
4290       switch (coneSize) {
4291       case 2:
4292         ct = DM_POLYTOPE_SEGMENT;
4293         break;
4294       case 3:
4295         ct = DM_POLYTOPE_TRIANGLE;
4296         break;
4297       case 4:
4298         ct = DM_POLYTOPE_QUADRILATERAL;
4299         break;
4300       default:
4301         break;
4302       }
4303     }
4304   }
4305   *pt = ct;
4306   PetscFunctionReturn(PETSC_SUCCESS);
4307 }
4308 
4309 /*@
4310   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4311 
4312   Collective on dm
4313 
4314   Input Parameter:
4315 . mesh - The `DMPLEX`
4316 
4317   Level: developer
4318 
4319   Note:
4320   This function is normally called automatically when a cell type is requested. It creates an
4321   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4322   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4323 
4324   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4325 
4326 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4327 @*/
4328 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4329 {
4330   DM_Plex *mesh;
4331   DMLabel  ctLabel;
4332   PetscInt pStart, pEnd, p;
4333 
4334   PetscFunctionBegin;
4335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4336   mesh = (DM_Plex *)dm->data;
4337   PetscCall(DMCreateLabel(dm, "celltype"));
4338   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4339   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4340   for (p = pStart; p < pEnd; ++p) {
4341     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4342     PetscInt       pdepth;
4343 
4344     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4345     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4346     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4347     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4348   }
4349   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4350   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4351   PetscFunctionReturn(PETSC_SUCCESS);
4352 }
4353 
4354 /*@C
4355   DMPlexGetJoin - Get an array for the join of the set of points
4356 
4357   Not Collective
4358 
4359   Input Parameters:
4360 + dm - The `DMPLEX` object
4361 . numPoints - The number of input points for the join
4362 - points - The input points
4363 
4364   Output Parameters:
4365 + numCoveredPoints - The number of points in the join
4366 - coveredPoints - The points in the join
4367 
4368   Level: intermediate
4369 
4370   Note:
4371   Currently, this is restricted to a single level join
4372 
4373   Fortran Note:
4374   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4375 
4376 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4377 @*/
4378 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4379 {
4380   DM_Plex  *mesh = (DM_Plex *)dm->data;
4381   PetscInt *join[2];
4382   PetscInt  joinSize, i = 0;
4383   PetscInt  dof, off, p, c, m;
4384   PetscInt  maxSupportSize;
4385 
4386   PetscFunctionBegin;
4387   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4388   PetscValidIntPointer(points, 3);
4389   PetscValidIntPointer(numCoveredPoints, 4);
4390   PetscValidPointer(coveredPoints, 5);
4391   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4392   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4393   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4394   /* Copy in support of first point */
4395   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4396   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4397   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4398   /* Check each successive support */
4399   for (p = 1; p < numPoints; ++p) {
4400     PetscInt newJoinSize = 0;
4401 
4402     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4403     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4404     for (c = 0; c < dof; ++c) {
4405       const PetscInt point = mesh->supports[off + c];
4406 
4407       for (m = 0; m < joinSize; ++m) {
4408         if (point == join[i][m]) {
4409           join[1 - i][newJoinSize++] = point;
4410           break;
4411         }
4412       }
4413     }
4414     joinSize = newJoinSize;
4415     i        = 1 - i;
4416   }
4417   *numCoveredPoints = joinSize;
4418   *coveredPoints    = join[i];
4419   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4420   PetscFunctionReturn(PETSC_SUCCESS);
4421 }
4422 
4423 /*@C
4424   DMPlexRestoreJoin - Restore an array for the join of the set of points
4425 
4426   Not Collective
4427 
4428   Input Parameters:
4429 + dm - The `DMPLEX` object
4430 . numPoints - The number of input points for the join
4431 - points - The input points
4432 
4433   Output Parameters:
4434 + numCoveredPoints - The number of points in the join
4435 - coveredPoints - The points in the join
4436 
4437   Level: intermediate
4438 
4439   Fortran Note:
4440   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4441 
4442 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4443 @*/
4444 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4445 {
4446   PetscFunctionBegin;
4447   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4448   if (points) PetscValidIntPointer(points, 3);
4449   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4450   PetscValidPointer(coveredPoints, 5);
4451   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4452   if (numCoveredPoints) *numCoveredPoints = 0;
4453   PetscFunctionReturn(PETSC_SUCCESS);
4454 }
4455 
4456 /*@C
4457   DMPlexGetFullJoin - Get an array for the join of the set of points
4458 
4459   Not Collective
4460 
4461   Input Parameters:
4462 + dm - The `DMPLEX` object
4463 . numPoints - The number of input points for the join
4464 - points - The input points
4465 
4466   Output Parameters:
4467 + numCoveredPoints - The number of points in the join
4468 - coveredPoints - The points in the join
4469 
4470   Level: intermediate
4471 
4472   Fortran Note:
4473   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4474 
4475 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4476 @*/
4477 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4478 {
4479   PetscInt *offsets, **closures;
4480   PetscInt *join[2];
4481   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4482   PetscInt  p, d, c, m, ms;
4483 
4484   PetscFunctionBegin;
4485   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4486   PetscValidIntPointer(points, 3);
4487   PetscValidIntPointer(numCoveredPoints, 4);
4488   PetscValidPointer(coveredPoints, 5);
4489 
4490   PetscCall(DMPlexGetDepth(dm, &depth));
4491   PetscCall(PetscCalloc1(numPoints, &closures));
4492   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4493   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4494   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4495   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4496   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4497 
4498   for (p = 0; p < numPoints; ++p) {
4499     PetscInt closureSize;
4500 
4501     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4502 
4503     offsets[p * (depth + 2) + 0] = 0;
4504     for (d = 0; d < depth + 1; ++d) {
4505       PetscInt pStart, pEnd, i;
4506 
4507       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4508       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4509         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4510           offsets[p * (depth + 2) + d + 1] = i;
4511           break;
4512         }
4513       }
4514       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4515     }
4516     PetscCheck(offsets[p * (depth + 2) + depth + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (depth + 2) + depth + 1], closureSize);
4517   }
4518   for (d = 0; d < depth + 1; ++d) {
4519     PetscInt dof;
4520 
4521     /* Copy in support of first point */
4522     dof = offsets[d + 1] - offsets[d];
4523     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4524     /* Check each successive cone */
4525     for (p = 1; p < numPoints && joinSize; ++p) {
4526       PetscInt newJoinSize = 0;
4527 
4528       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4529       for (c = 0; c < dof; ++c) {
4530         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4531 
4532         for (m = 0; m < joinSize; ++m) {
4533           if (point == join[i][m]) {
4534             join[1 - i][newJoinSize++] = point;
4535             break;
4536           }
4537         }
4538       }
4539       joinSize = newJoinSize;
4540       i        = 1 - i;
4541     }
4542     if (joinSize) break;
4543   }
4544   *numCoveredPoints = joinSize;
4545   *coveredPoints    = join[i];
4546   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4547   PetscCall(PetscFree(closures));
4548   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4549   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4550   PetscFunctionReturn(PETSC_SUCCESS);
4551 }
4552 
4553 /*@C
4554   DMPlexGetMeet - Get an array for the meet of the set of points
4555 
4556   Not Collective
4557 
4558   Input Parameters:
4559 + dm - The `DMPLEX` object
4560 . numPoints - The number of input points for the meet
4561 - points - The input points
4562 
4563   Output Parameters:
4564 + numCoveredPoints - The number of points in the meet
4565 - coveredPoints - The points in the meet
4566 
4567   Level: intermediate
4568 
4569   Note:
4570   Currently, this is restricted to a single level meet
4571 
4572   Fortran Notes:
4573   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4574 
4575 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4576 @*/
4577 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4578 {
4579   DM_Plex  *mesh = (DM_Plex *)dm->data;
4580   PetscInt *meet[2];
4581   PetscInt  meetSize, i = 0;
4582   PetscInt  dof, off, p, c, m;
4583   PetscInt  maxConeSize;
4584 
4585   PetscFunctionBegin;
4586   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4587   PetscValidIntPointer(points, 3);
4588   PetscValidIntPointer(numCoveringPoints, 4);
4589   PetscValidPointer(coveringPoints, 5);
4590   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4591   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4592   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4593   /* Copy in cone of first point */
4594   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4595   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4596   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4597   /* Check each successive cone */
4598   for (p = 1; p < numPoints; ++p) {
4599     PetscInt newMeetSize = 0;
4600 
4601     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4602     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4603     for (c = 0; c < dof; ++c) {
4604       const PetscInt point = mesh->cones[off + c];
4605 
4606       for (m = 0; m < meetSize; ++m) {
4607         if (point == meet[i][m]) {
4608           meet[1 - i][newMeetSize++] = point;
4609           break;
4610         }
4611       }
4612     }
4613     meetSize = newMeetSize;
4614     i        = 1 - i;
4615   }
4616   *numCoveringPoints = meetSize;
4617   *coveringPoints    = meet[i];
4618   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4619   PetscFunctionReturn(PETSC_SUCCESS);
4620 }
4621 
4622 /*@C
4623   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4624 
4625   Not Collective
4626 
4627   Input Parameters:
4628 + dm - The `DMPLEX` object
4629 . numPoints - The number of input points for the meet
4630 - points - The input points
4631 
4632   Output Parameters:
4633 + numCoveredPoints - The number of points in the meet
4634 - coveredPoints - The points in the meet
4635 
4636   Level: intermediate
4637 
4638   Fortran Note:
4639   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4640 
4641 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4642 @*/
4643 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4644 {
4645   PetscFunctionBegin;
4646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4647   if (points) PetscValidIntPointer(points, 3);
4648   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4649   PetscValidPointer(coveredPoints, 5);
4650   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4651   if (numCoveredPoints) *numCoveredPoints = 0;
4652   PetscFunctionReturn(PETSC_SUCCESS);
4653 }
4654 
4655 /*@C
4656   DMPlexGetFullMeet - Get an array for the meet of the set of points
4657 
4658   Not Collective
4659 
4660   Input Parameters:
4661 + dm - The `DMPLEX` object
4662 . numPoints - The number of input points for the meet
4663 - points - The input points
4664 
4665   Output Parameters:
4666 + numCoveredPoints - The number of points in the meet
4667 - coveredPoints - The points in the meet
4668 
4669   Level: intermediate
4670 
4671   Fortran Note:
4672   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4673 
4674 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4675 @*/
4676 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4677 {
4678   PetscInt *offsets, **closures;
4679   PetscInt *meet[2];
4680   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4681   PetscInt  p, h, c, m, mc;
4682 
4683   PetscFunctionBegin;
4684   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4685   PetscValidIntPointer(points, 3);
4686   PetscValidIntPointer(numCoveredPoints, 4);
4687   PetscValidPointer(coveredPoints, 5);
4688 
4689   PetscCall(DMPlexGetDepth(dm, &height));
4690   PetscCall(PetscMalloc1(numPoints, &closures));
4691   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4692   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4693   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4694   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4695   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4696 
4697   for (p = 0; p < numPoints; ++p) {
4698     PetscInt closureSize;
4699 
4700     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4701 
4702     offsets[p * (height + 2) + 0] = 0;
4703     for (h = 0; h < height + 1; ++h) {
4704       PetscInt pStart, pEnd, i;
4705 
4706       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4707       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4708         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4709           offsets[p * (height + 2) + h + 1] = i;
4710           break;
4711         }
4712       }
4713       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4714     }
4715     PetscCheck(offsets[p * (height + 2) + height + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (height + 2) + height + 1], closureSize);
4716   }
4717   for (h = 0; h < height + 1; ++h) {
4718     PetscInt dof;
4719 
4720     /* Copy in cone of first point */
4721     dof = offsets[h + 1] - offsets[h];
4722     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4723     /* Check each successive cone */
4724     for (p = 1; p < numPoints && meetSize; ++p) {
4725       PetscInt newMeetSize = 0;
4726 
4727       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4728       for (c = 0; c < dof; ++c) {
4729         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4730 
4731         for (m = 0; m < meetSize; ++m) {
4732           if (point == meet[i][m]) {
4733             meet[1 - i][newMeetSize++] = point;
4734             break;
4735           }
4736         }
4737       }
4738       meetSize = newMeetSize;
4739       i        = 1 - i;
4740     }
4741     if (meetSize) break;
4742   }
4743   *numCoveredPoints = meetSize;
4744   *coveredPoints    = meet[i];
4745   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4746   PetscCall(PetscFree(closures));
4747   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4748   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4749   PetscFunctionReturn(PETSC_SUCCESS);
4750 }
4751 
4752 /*@C
4753   DMPlexEqual - Determine if two `DM` have the same topology
4754 
4755   Not Collective
4756 
4757   Input Parameters:
4758 + dmA - A `DMPLEX` object
4759 - dmB - A `DMPLEX` object
4760 
4761   Output Parameters:
4762 . equal - `PETSC_TRUE` if the topologies are identical
4763 
4764   Level: intermediate
4765 
4766   Note:
4767   We are not solving graph isomorphism, so we do not permute.
4768 
4769 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4770 @*/
4771 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4772 {
4773   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4774 
4775   PetscFunctionBegin;
4776   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4777   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4778   PetscValidBoolPointer(equal, 3);
4779 
4780   *equal = PETSC_FALSE;
4781   PetscCall(DMPlexGetDepth(dmA, &depth));
4782   PetscCall(DMPlexGetDepth(dmB, &depthB));
4783   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4784   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4785   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4786   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4787   for (p = pStart; p < pEnd; ++p) {
4788     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4789     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4790 
4791     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4792     PetscCall(DMPlexGetCone(dmA, p, &cone));
4793     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4794     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4795     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4796     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4797     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4798     for (c = 0; c < coneSize; ++c) {
4799       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4800       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4801     }
4802     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4803     PetscCall(DMPlexGetSupport(dmA, p, &support));
4804     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4805     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4806     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4807     for (s = 0; s < supportSize; ++s) {
4808       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4809     }
4810   }
4811   *equal = PETSC_TRUE;
4812   PetscFunctionReturn(PETSC_SUCCESS);
4813 }
4814 
4815 /*@C
4816   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4817 
4818   Not Collective
4819 
4820   Input Parameters:
4821 + dm         - The `DMPLEX`
4822 . cellDim    - The cell dimension
4823 - numCorners - The number of vertices on a cell
4824 
4825   Output Parameters:
4826 . numFaceVertices - The number of vertices on a face
4827 
4828   Level: developer
4829 
4830   Note:
4831   Of course this can only work for a restricted set of symmetric shapes
4832 
4833 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4834 @*/
4835 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4836 {
4837   MPI_Comm comm;
4838 
4839   PetscFunctionBegin;
4840   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4841   PetscValidIntPointer(numFaceVertices, 4);
4842   switch (cellDim) {
4843   case 0:
4844     *numFaceVertices = 0;
4845     break;
4846   case 1:
4847     *numFaceVertices = 1;
4848     break;
4849   case 2:
4850     switch (numCorners) {
4851     case 3:                 /* triangle */
4852       *numFaceVertices = 2; /* Edge has 2 vertices */
4853       break;
4854     case 4:                 /* quadrilateral */
4855       *numFaceVertices = 2; /* Edge has 2 vertices */
4856       break;
4857     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4858       *numFaceVertices = 3; /* Edge has 3 vertices */
4859       break;
4860     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4861       *numFaceVertices = 3; /* Edge has 3 vertices */
4862       break;
4863     default:
4864       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4865     }
4866     break;
4867   case 3:
4868     switch (numCorners) {
4869     case 4:                 /* tetradehdron */
4870       *numFaceVertices = 3; /* Face has 3 vertices */
4871       break;
4872     case 6:                 /* tet cohesive cells */
4873       *numFaceVertices = 4; /* Face has 4 vertices */
4874       break;
4875     case 8:                 /* hexahedron */
4876       *numFaceVertices = 4; /* Face has 4 vertices */
4877       break;
4878     case 9:                 /* tet cohesive Lagrange cells */
4879       *numFaceVertices = 6; /* Face has 6 vertices */
4880       break;
4881     case 10:                /* quadratic tetrahedron */
4882       *numFaceVertices = 6; /* Face has 6 vertices */
4883       break;
4884     case 12:                /* hex cohesive Lagrange cells */
4885       *numFaceVertices = 6; /* Face has 6 vertices */
4886       break;
4887     case 18:                /* quadratic tet cohesive Lagrange cells */
4888       *numFaceVertices = 6; /* Face has 6 vertices */
4889       break;
4890     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4891       *numFaceVertices = 9; /* Face has 9 vertices */
4892       break;
4893     default:
4894       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4895     }
4896     break;
4897   default:
4898     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4899   }
4900   PetscFunctionReturn(PETSC_SUCCESS);
4901 }
4902 
4903 /*@
4904   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4905 
4906   Not Collective
4907 
4908   Input Parameter:
4909 . dm    - The `DMPLEX` object
4910 
4911   Output Parameter:
4912 . depthLabel - The `DMLabel` recording point depth
4913 
4914   Level: developer
4915 
4916 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4917 @*/
4918 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4919 {
4920   PetscFunctionBegin;
4921   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4922   PetscValidPointer(depthLabel, 2);
4923   *depthLabel = dm->depthLabel;
4924   PetscFunctionReturn(PETSC_SUCCESS);
4925 }
4926 
4927 /*@
4928   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4929 
4930   Not Collective
4931 
4932   Input Parameter:
4933 . dm    - The `DMPLEX` object
4934 
4935   Output Parameter:
4936 . depth - The number of strata (breadth first levels) in the DAG
4937 
4938   Level: developer
4939 
4940   Notes:
4941   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4942 
4943   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4944 
4945   An empty mesh gives -1.
4946 
4947 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4948 @*/
4949 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4950 {
4951   DM_Plex *mesh = (DM_Plex *)dm->data;
4952   DMLabel  label;
4953   PetscInt d = 0;
4954 
4955   PetscFunctionBegin;
4956   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4957   PetscValidIntPointer(depth, 2);
4958   if (mesh->tr) {
4959     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4960   } else {
4961     PetscCall(DMPlexGetDepthLabel(dm, &label));
4962     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4963     *depth = d - 1;
4964   }
4965   PetscFunctionReturn(PETSC_SUCCESS);
4966 }
4967 
4968 /*@
4969   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4970 
4971   Not Collective
4972 
4973   Input Parameters:
4974 + dm    - The `DMPLEX` object
4975 - depth - The requested depth
4976 
4977   Output Parameters:
4978 + start - The first point at this depth
4979 - end   - One beyond the last point at this depth
4980 
4981   Level: developer
4982 
4983   Notes:
4984   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4985   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
4986   higher dimension, e.g., "edges".
4987 
4988 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4989 @*/
4990 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4991 {
4992   DM_Plex *mesh = (DM_Plex *)dm->data;
4993   DMLabel  label;
4994   PetscInt pStart, pEnd;
4995 
4996   PetscFunctionBegin;
4997   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4998   if (start) {
4999     PetscValidIntPointer(start, 3);
5000     *start = 0;
5001   }
5002   if (end) {
5003     PetscValidIntPointer(end, 4);
5004     *end = 0;
5005   }
5006   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5007   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5008   if (depth < 0) {
5009     if (start) *start = pStart;
5010     if (end) *end = pEnd;
5011     PetscFunctionReturn(PETSC_SUCCESS);
5012   }
5013   if (mesh->tr) {
5014     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5015   } else {
5016     PetscCall(DMPlexGetDepthLabel(dm, &label));
5017     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5018     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5019   }
5020   PetscFunctionReturn(PETSC_SUCCESS);
5021 }
5022 
5023 /*@
5024   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
5025 
5026   Not Collective
5027 
5028   Input Parameters:
5029 + dm     - The `DMPLEX` object
5030 - height - The requested height
5031 
5032   Output Parameters:
5033 + start - The first point at this height
5034 - end   - One beyond the last point at this height
5035 
5036   Level: developer
5037 
5038   Notes:
5039   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5040   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5041   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5042 
5043 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5044 @*/
5045 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5046 {
5047   DMLabel  label;
5048   PetscInt depth, pStart, pEnd;
5049 
5050   PetscFunctionBegin;
5051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5052   if (start) {
5053     PetscValidIntPointer(start, 3);
5054     *start = 0;
5055   }
5056   if (end) {
5057     PetscValidIntPointer(end, 4);
5058     *end = 0;
5059   }
5060   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5061   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5062   if (height < 0) {
5063     if (start) *start = pStart;
5064     if (end) *end = pEnd;
5065     PetscFunctionReturn(PETSC_SUCCESS);
5066   }
5067   PetscCall(DMPlexGetDepthLabel(dm, &label));
5068   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5069   PetscCall(DMLabelGetNumValues(label, &depth));
5070   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5071   PetscFunctionReturn(PETSC_SUCCESS);
5072 }
5073 
5074 /*@
5075   DMPlexGetPointDepth - Get the depth of a given point
5076 
5077   Not Collective
5078 
5079   Input Parameters:
5080 + dm    - The `DMPLEX` object
5081 - point - The point
5082 
5083   Output Parameter:
5084 . depth - The depth of the point
5085 
5086   Level: intermediate
5087 
5088 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5089 @*/
5090 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5091 {
5092   PetscFunctionBegin;
5093   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5094   PetscValidIntPointer(depth, 3);
5095   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5096   PetscFunctionReturn(PETSC_SUCCESS);
5097 }
5098 
5099 /*@
5100   DMPlexGetPointHeight - Get the height of a given point
5101 
5102   Not Collective
5103 
5104   Input Parameters:
5105 + dm    - The `DMPLEX` object
5106 - point - The point
5107 
5108   Output Parameter:
5109 . height - The height of the point
5110 
5111   Level: intermediate
5112 
5113 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5114 @*/
5115 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5116 {
5117   PetscInt n, pDepth;
5118 
5119   PetscFunctionBegin;
5120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5121   PetscValidIntPointer(height, 3);
5122   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5123   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5124   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5125   PetscFunctionReturn(PETSC_SUCCESS);
5126 }
5127 
5128 /*@
5129   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5130 
5131   Not Collective
5132 
5133   Input Parameter:
5134 . dm - The `DMPLEX` object
5135 
5136   Output Parameter:
5137 . celltypeLabel - The `DMLabel` recording cell polytope type
5138 
5139   Level: developer
5140 
5141   Note:
5142   This function will trigger automatica computation of cell types. This can be disabled by calling
5143   `DMCreateLabel`(dm, "celltype") beforehand.
5144 
5145 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5146 @*/
5147 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5148 {
5149   PetscFunctionBegin;
5150   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5151   PetscValidPointer(celltypeLabel, 2);
5152   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5153   *celltypeLabel = dm->celltypeLabel;
5154   PetscFunctionReturn(PETSC_SUCCESS);
5155 }
5156 
5157 /*@
5158   DMPlexGetCellType - Get the polytope type of a given cell
5159 
5160   Not Collective
5161 
5162   Input Parameters:
5163 + dm   - The `DMPLEX` object
5164 - cell - The cell
5165 
5166   Output Parameter:
5167 . celltype - The polytope type of the cell
5168 
5169   Level: intermediate
5170 
5171 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5172 @*/
5173 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5174 {
5175   DM_Plex *mesh = (DM_Plex *)dm->data;
5176   DMLabel  label;
5177   PetscInt ct;
5178 
5179   PetscFunctionBegin;
5180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5181   PetscValidPointer(celltype, 3);
5182   if (mesh->tr) {
5183     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5184   } else {
5185     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5186     PetscCall(DMLabelGetValue(label, cell, &ct));
5187     PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5188     *celltype = (DMPolytopeType)ct;
5189   }
5190   PetscFunctionReturn(PETSC_SUCCESS);
5191 }
5192 
5193 /*@
5194   DMPlexSetCellType - Set the polytope type of a given cell
5195 
5196   Not Collective
5197 
5198   Input Parameters:
5199 + dm   - The `DMPLEX` object
5200 . cell - The cell
5201 - celltype - The polytope type of the cell
5202 
5203   Level: advanced
5204 
5205   Note:
5206   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5207   is executed. This function will override the computed type. However, if automatic classification will not succeed
5208   and a user wants to manually specify all types, the classification must be disabled by calling
5209   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5210 
5211 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5212 @*/
5213 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5214 {
5215   DMLabel label;
5216 
5217   PetscFunctionBegin;
5218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5219   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5220   PetscCall(DMLabelSetValue(label, cell, celltype));
5221   PetscFunctionReturn(PETSC_SUCCESS);
5222 }
5223 
5224 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5225 {
5226   PetscSection section, s;
5227   Mat          m;
5228   PetscInt     maxHeight;
5229   const char  *prefix;
5230 
5231   PetscFunctionBegin;
5232   PetscCall(DMClone(dm, cdm));
5233   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5234   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5235   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5236   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5237   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5238   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5239   PetscCall(DMSetLocalSection(*cdm, section));
5240   PetscCall(PetscSectionDestroy(&section));
5241   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5242   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5243   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5244   PetscCall(PetscSectionDestroy(&s));
5245   PetscCall(MatDestroy(&m));
5246 
5247   PetscCall(DMSetNumFields(*cdm, 1));
5248   PetscCall(DMCreateDS(*cdm));
5249   (*cdm)->cloneOpts = PETSC_TRUE;
5250   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5251   PetscFunctionReturn(PETSC_SUCCESS);
5252 }
5253 
5254 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5255 {
5256   Vec coordsLocal, cellCoordsLocal;
5257   DM  coordsDM, cellCoordsDM;
5258 
5259   PetscFunctionBegin;
5260   *field = NULL;
5261   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5262   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5263   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5264   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5265   if (coordsLocal && coordsDM) {
5266     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5267     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5268   }
5269   PetscFunctionReturn(PETSC_SUCCESS);
5270 }
5271 
5272 /*@C
5273   DMPlexGetConeSection - Return a section which describes the layout of cone data
5274 
5275   Not Collective
5276 
5277   Input Parameters:
5278 . dm        - The `DMPLEX` object
5279 
5280   Output Parameter:
5281 . section - The `PetscSection` object
5282 
5283   Level: developer
5284 
5285 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5286 @*/
5287 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5288 {
5289   DM_Plex *mesh = (DM_Plex *)dm->data;
5290 
5291   PetscFunctionBegin;
5292   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5293   if (section) *section = mesh->coneSection;
5294   PetscFunctionReturn(PETSC_SUCCESS);
5295 }
5296 
5297 /*@C
5298   DMPlexGetSupportSection - Return a section which describes the layout of support data
5299 
5300   Not Collective
5301 
5302   Input Parameters:
5303 . dm        - The `DMPLEX` object
5304 
5305   Output Parameter:
5306 . section - The `PetscSection` object
5307 
5308   Level: developer
5309 
5310 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5311 @*/
5312 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5313 {
5314   DM_Plex *mesh = (DM_Plex *)dm->data;
5315 
5316   PetscFunctionBegin;
5317   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5318   if (section) *section = mesh->supportSection;
5319   PetscFunctionReturn(PETSC_SUCCESS);
5320 }
5321 
5322 /*@C
5323   DMPlexGetCones - Return cone data
5324 
5325   Not Collective
5326 
5327   Input Parameters:
5328 . dm        - The `DMPLEX` object
5329 
5330   Output Parameter:
5331 . cones - The cone for each point
5332 
5333   Level: developer
5334 
5335 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5336 @*/
5337 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5338 {
5339   DM_Plex *mesh = (DM_Plex *)dm->data;
5340 
5341   PetscFunctionBegin;
5342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5343   if (cones) *cones = mesh->cones;
5344   PetscFunctionReturn(PETSC_SUCCESS);
5345 }
5346 
5347 /*@C
5348   DMPlexGetConeOrientations - Return cone orientation data
5349 
5350   Not Collective
5351 
5352   Input Parameters:
5353 . dm        - The `DMPLEX` object
5354 
5355   Output Parameter:
5356 . coneOrientations - The array of cone orientations for all points
5357 
5358   Level: developer
5359 
5360   Notes:
5361   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5362 
5363   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5364 
5365 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5366 @*/
5367 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5368 {
5369   DM_Plex *mesh = (DM_Plex *)dm->data;
5370 
5371   PetscFunctionBegin;
5372   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5373   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5374   PetscFunctionReturn(PETSC_SUCCESS);
5375 }
5376 
5377 /******************************** FEM Support **********************************/
5378 
5379 /*
5380  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5381  representing a line in the section.
5382 */
5383 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5384 {
5385   PetscFunctionBeginHot;
5386   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5387   if (line < 0) {
5388     *k  = 0;
5389     *Nc = 0;
5390   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5391     *k = 1;
5392   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5393     /* An order k SEM disc has k-1 dofs on an edge */
5394     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5395     *k = *k / *Nc + 1;
5396   }
5397   PetscFunctionReturn(PETSC_SUCCESS);
5398 }
5399 
5400 /*@
5401 
5402   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5403   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5404   section provided (or the section of the DM).
5405 
5406   Input Parameters:
5407 + dm      - The DM
5408 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5409 - section - The PetscSection to reorder, or NULL for the default section
5410 
5411   Example:
5412   A typical interpolated single-quad mesh might order points as
5413 .vb
5414   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5415 
5416   v4 -- e6 -- v3
5417   |           |
5418   e7    c0    e8
5419   |           |
5420   v1 -- e5 -- v2
5421 .ve
5422 
5423   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5424   dofs in the order of points, e.g.,
5425 .vb
5426     c0 -> [0,1,2,3]
5427     v1 -> [4]
5428     ...
5429     e5 -> [8, 9]
5430 .ve
5431 
5432   which corresponds to the dofs
5433 .vb
5434     6   10  11  7
5435     13  2   3   15
5436     12  0   1   14
5437     4   8   9   5
5438 .ve
5439 
5440   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5441 .vb
5442   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5443 .ve
5444 
5445   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5446 .vb
5447    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5448 .ve
5449 
5450   Level: developer
5451 
5452   Note:
5453   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5454   degree of the basis.
5455 
5456 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5457 @*/
5458 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5459 {
5460   DMLabel   label;
5461   PetscInt  dim, depth = -1, eStart = -1, Nf;
5462   PetscBool vertexchart;
5463 
5464   PetscFunctionBegin;
5465   PetscCall(DMGetDimension(dm, &dim));
5466   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5467   if (point < 0) {
5468     PetscInt sStart, sEnd;
5469 
5470     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5471     point = sEnd - sStart ? sStart : point;
5472   }
5473   PetscCall(DMPlexGetDepthLabel(dm, &label));
5474   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5475   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5476   if (depth == 1) {
5477     eStart = point;
5478   } else if (depth == dim) {
5479     const PetscInt *cone;
5480 
5481     PetscCall(DMPlexGetCone(dm, point, &cone));
5482     if (dim == 2) eStart = cone[0];
5483     else if (dim == 3) {
5484       const PetscInt *cone2;
5485       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5486       eStart = cone2[0];
5487     } 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);
5488   } 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);
5489   { /* Determine whether the chart covers all points or just vertices. */
5490     PetscInt pStart, pEnd, cStart, cEnd;
5491     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5492     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5493     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5494     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5495     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5496   }
5497   PetscCall(PetscSectionGetNumFields(section, &Nf));
5498   for (PetscInt d = 1; d <= dim; d++) {
5499     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5500     PetscInt *perm;
5501 
5502     for (f = 0; f < Nf; ++f) {
5503       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5504       size += PetscPowInt(k + 1, d) * Nc;
5505     }
5506     PetscCall(PetscMalloc1(size, &perm));
5507     for (f = 0; f < Nf; ++f) {
5508       switch (d) {
5509       case 1:
5510         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5511         /*
5512          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5513          We want              [ vtx0; edge of length k-1; vtx1 ]
5514          */
5515         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5516         for (i = 0; i < k - 1; i++)
5517           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5518         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5519         foffset = offset;
5520         break;
5521       case 2:
5522         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5523         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5524         /* The SEM order is
5525 
5526          v_lb, {e_b}, v_rb,
5527          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5528          v_lt, reverse {e_t}, v_rt
5529          */
5530         {
5531           const PetscInt of   = 0;
5532           const PetscInt oeb  = of + PetscSqr(k - 1);
5533           const PetscInt oer  = oeb + (k - 1);
5534           const PetscInt oet  = oer + (k - 1);
5535           const PetscInt oel  = oet + (k - 1);
5536           const PetscInt ovlb = oel + (k - 1);
5537           const PetscInt ovrb = ovlb + 1;
5538           const PetscInt ovrt = ovrb + 1;
5539           const PetscInt ovlt = ovrt + 1;
5540           PetscInt       o;
5541 
5542           /* bottom */
5543           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5544           for (o = oeb; o < oer; ++o)
5545             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5546           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5547           /* middle */
5548           for (i = 0; i < k - 1; ++i) {
5549             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5550             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5551               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5552             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5553           }
5554           /* top */
5555           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5556           for (o = oel - 1; o >= oet; --o)
5557             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5558           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5559           foffset = offset;
5560         }
5561         break;
5562       case 3:
5563         /* The original hex closure is
5564 
5565          {c,
5566          f_b, f_t, f_f, f_b, f_r, f_l,
5567          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5568          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5569          */
5570         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5571         /* The SEM order is
5572          Bottom Slice
5573          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5574          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5575          v_blb, {e_bb}, v_brb,
5576 
5577          Middle Slice (j)
5578          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5579          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5580          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5581 
5582          Top Slice
5583          v_tlf, {e_tf}, v_trf,
5584          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5585          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5586          */
5587         {
5588           const PetscInt oc    = 0;
5589           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5590           const PetscInt oft   = ofb + PetscSqr(k - 1);
5591           const PetscInt off   = oft + PetscSqr(k - 1);
5592           const PetscInt ofk   = off + PetscSqr(k - 1);
5593           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5594           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5595           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5596           const PetscInt oebb  = oebl + (k - 1);
5597           const PetscInt oebr  = oebb + (k - 1);
5598           const PetscInt oebf  = oebr + (k - 1);
5599           const PetscInt oetf  = oebf + (k - 1);
5600           const PetscInt oetr  = oetf + (k - 1);
5601           const PetscInt oetb  = oetr + (k - 1);
5602           const PetscInt oetl  = oetb + (k - 1);
5603           const PetscInt oerf  = oetl + (k - 1);
5604           const PetscInt oelf  = oerf + (k - 1);
5605           const PetscInt oelb  = oelf + (k - 1);
5606           const PetscInt oerb  = oelb + (k - 1);
5607           const PetscInt ovblf = oerb + (k - 1);
5608           const PetscInt ovblb = ovblf + 1;
5609           const PetscInt ovbrb = ovblb + 1;
5610           const PetscInt ovbrf = ovbrb + 1;
5611           const PetscInt ovtlf = ovbrf + 1;
5612           const PetscInt ovtrf = ovtlf + 1;
5613           const PetscInt ovtrb = ovtrf + 1;
5614           const PetscInt ovtlb = ovtrb + 1;
5615           PetscInt       o, n;
5616 
5617           /* Bottom Slice */
5618           /*   bottom */
5619           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5620           for (o = oetf - 1; o >= oebf; --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] = ovbrf * Nc + c + foffset;
5623           /*   middle */
5624           for (i = 0; i < k - 1; ++i) {
5625             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5626             for (n = 0; n < k - 1; ++n) {
5627               o = ofb + n * (k - 1) + i;
5628               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5629             }
5630             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5631           }
5632           /*   top */
5633           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5634           for (o = oebb; o < oebr; ++o)
5635             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5636           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5637 
5638           /* Middle Slice */
5639           for (j = 0; j < k - 1; ++j) {
5640             /*   bottom */
5641             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5642             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5643               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5644             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5645             /*   middle */
5646             for (i = 0; i < k - 1; ++i) {
5647               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5648               for (n = 0; n < k - 1; ++n)
5649                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5650               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5651             }
5652             /*   top */
5653             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5654             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5655               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5656             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5657           }
5658 
5659           /* Top Slice */
5660           /*   bottom */
5661           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5662           for (o = oetf; o < oetr; ++o)
5663             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5664           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5665           /*   middle */
5666           for (i = 0; i < k - 1; ++i) {
5667             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5668             for (n = 0; n < k - 1; ++n)
5669               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5670             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5671           }
5672           /*   top */
5673           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5674           for (o = oetl - 1; o >= oetb; --o)
5675             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5676           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5677 
5678           foffset = offset;
5679         }
5680         break;
5681       default:
5682         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5683       }
5684     }
5685     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5686     /* Check permutation */
5687     {
5688       PetscInt *check;
5689 
5690       PetscCall(PetscMalloc1(size, &check));
5691       for (i = 0; i < size; ++i) {
5692         check[i] = -1;
5693         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5694       }
5695       for (i = 0; i < size; ++i) check[perm[i]] = i;
5696       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5697       PetscCall(PetscFree(check));
5698     }
5699     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5700     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5701       PetscInt *loc_perm;
5702       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5703       for (PetscInt i = 0; i < size; i++) {
5704         loc_perm[i]        = perm[i];
5705         loc_perm[size + i] = size + perm[i];
5706       }
5707       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5708     }
5709   }
5710   PetscFunctionReturn(PETSC_SUCCESS);
5711 }
5712 
5713 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5714 {
5715   PetscDS  prob;
5716   PetscInt depth, Nf, h;
5717   DMLabel  label;
5718 
5719   PetscFunctionBeginHot;
5720   PetscCall(DMGetDS(dm, &prob));
5721   Nf      = prob->Nf;
5722   label   = dm->depthLabel;
5723   *dspace = NULL;
5724   if (field < Nf) {
5725     PetscObject disc = prob->disc[field];
5726 
5727     if (disc->classid == PETSCFE_CLASSID) {
5728       PetscDualSpace dsp;
5729 
5730       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5731       PetscCall(DMLabelGetNumValues(label, &depth));
5732       PetscCall(DMLabelGetValue(label, point, &h));
5733       h = depth - 1 - h;
5734       if (h) {
5735         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5736       } else {
5737         *dspace = dsp;
5738       }
5739     }
5740   }
5741   PetscFunctionReturn(PETSC_SUCCESS);
5742 }
5743 
5744 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5745 {
5746   PetscScalar       *array;
5747   const PetscScalar *vArray;
5748   const PetscInt    *cone, *coneO;
5749   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5750 
5751   PetscFunctionBeginHot;
5752   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5753   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5754   PetscCall(DMPlexGetCone(dm, point, &cone));
5755   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5756   if (!values || !*values) {
5757     if ((point >= pStart) && (point < pEnd)) {
5758       PetscInt dof;
5759 
5760       PetscCall(PetscSectionGetDof(section, point, &dof));
5761       size += dof;
5762     }
5763     for (p = 0; p < numPoints; ++p) {
5764       const PetscInt cp = cone[p];
5765       PetscInt       dof;
5766 
5767       if ((cp < pStart) || (cp >= pEnd)) continue;
5768       PetscCall(PetscSectionGetDof(section, cp, &dof));
5769       size += dof;
5770     }
5771     if (!values) {
5772       if (csize) *csize = size;
5773       PetscFunctionReturn(PETSC_SUCCESS);
5774     }
5775     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5776   } else {
5777     array = *values;
5778   }
5779   size = 0;
5780   PetscCall(VecGetArrayRead(v, &vArray));
5781   if ((point >= pStart) && (point < pEnd)) {
5782     PetscInt           dof, off, d;
5783     const PetscScalar *varr;
5784 
5785     PetscCall(PetscSectionGetDof(section, point, &dof));
5786     PetscCall(PetscSectionGetOffset(section, point, &off));
5787     varr = &vArray[off];
5788     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5789     size += dof;
5790   }
5791   for (p = 0; p < numPoints; ++p) {
5792     const PetscInt     cp = cone[p];
5793     PetscInt           o  = coneO[p];
5794     PetscInt           dof, off, d;
5795     const PetscScalar *varr;
5796 
5797     if ((cp < pStart) || (cp >= pEnd)) continue;
5798     PetscCall(PetscSectionGetDof(section, cp, &dof));
5799     PetscCall(PetscSectionGetOffset(section, cp, &off));
5800     varr = &vArray[off];
5801     if (o >= 0) {
5802       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5803     } else {
5804       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5805     }
5806     size += dof;
5807   }
5808   PetscCall(VecRestoreArrayRead(v, &vArray));
5809   if (!*values) {
5810     if (csize) *csize = size;
5811     *values = array;
5812   } else {
5813     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5814     *csize = size;
5815   }
5816   PetscFunctionReturn(PETSC_SUCCESS);
5817 }
5818 
5819 /* Compress out points not in the section */
5820 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5821 {
5822   const PetscInt np = *numPoints;
5823   PetscInt       pStart, pEnd, p, q;
5824 
5825   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5826   for (p = 0, q = 0; p < np; ++p) {
5827     const PetscInt r = points[p * 2];
5828     if ((r >= pStart) && (r < pEnd)) {
5829       points[q * 2]     = r;
5830       points[q * 2 + 1] = points[p * 2 + 1];
5831       ++q;
5832     }
5833   }
5834   *numPoints = q;
5835   return PETSC_SUCCESS;
5836 }
5837 
5838 /* Compressed closure does not apply closure permutation */
5839 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5840 {
5841   const PetscInt *cla = NULL;
5842   PetscInt        np, *pts = NULL;
5843 
5844   PetscFunctionBeginHot;
5845   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5846   if (*clPoints) {
5847     PetscInt dof, off;
5848 
5849     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5850     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5851     PetscCall(ISGetIndices(*clPoints, &cla));
5852     np  = dof / 2;
5853     pts = (PetscInt *)&cla[off];
5854   } else {
5855     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5856     PetscCall(CompressPoints_Private(section, &np, pts));
5857   }
5858   *numPoints = np;
5859   *points    = pts;
5860   *clp       = cla;
5861   PetscFunctionReturn(PETSC_SUCCESS);
5862 }
5863 
5864 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5865 {
5866   PetscFunctionBeginHot;
5867   if (!*clPoints) {
5868     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5869   } else {
5870     PetscCall(ISRestoreIndices(*clPoints, clp));
5871   }
5872   *numPoints = 0;
5873   *points    = NULL;
5874   *clSec     = NULL;
5875   *clPoints  = NULL;
5876   *clp       = NULL;
5877   PetscFunctionReturn(PETSC_SUCCESS);
5878 }
5879 
5880 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5881 {
5882   PetscInt            offset = 0, p;
5883   const PetscInt    **perms  = NULL;
5884   const PetscScalar **flips  = NULL;
5885 
5886   PetscFunctionBeginHot;
5887   *size = 0;
5888   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5889   for (p = 0; p < numPoints; p++) {
5890     const PetscInt     point = points[2 * p];
5891     const PetscInt    *perm  = perms ? perms[p] : NULL;
5892     const PetscScalar *flip  = flips ? flips[p] : NULL;
5893     PetscInt           dof, off, d;
5894     const PetscScalar *varr;
5895 
5896     PetscCall(PetscSectionGetDof(section, point, &dof));
5897     PetscCall(PetscSectionGetOffset(section, point, &off));
5898     varr = &vArray[off];
5899     if (clperm) {
5900       if (perm) {
5901         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5902       } else {
5903         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5904       }
5905       if (flip) {
5906         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5907       }
5908     } else {
5909       if (perm) {
5910         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5911       } else {
5912         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5913       }
5914       if (flip) {
5915         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5916       }
5917     }
5918     offset += dof;
5919   }
5920   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5921   *size = offset;
5922   PetscFunctionReturn(PETSC_SUCCESS);
5923 }
5924 
5925 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[])
5926 {
5927   PetscInt offset = 0, f;
5928 
5929   PetscFunctionBeginHot;
5930   *size = 0;
5931   for (f = 0; f < numFields; ++f) {
5932     PetscInt            p;
5933     const PetscInt    **perms = NULL;
5934     const PetscScalar **flips = NULL;
5935 
5936     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5937     for (p = 0; p < numPoints; p++) {
5938       const PetscInt     point = points[2 * p];
5939       PetscInt           fdof, foff, b;
5940       const PetscScalar *varr;
5941       const PetscInt    *perm = perms ? perms[p] : NULL;
5942       const PetscScalar *flip = flips ? flips[p] : NULL;
5943 
5944       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5945       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5946       varr = &vArray[foff];
5947       if (clperm) {
5948         if (perm) {
5949           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5950         } else {
5951           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5952         }
5953         if (flip) {
5954           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5955         }
5956       } else {
5957         if (perm) {
5958           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5959         } else {
5960           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5961         }
5962         if (flip) {
5963           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5964         }
5965       }
5966       offset += fdof;
5967     }
5968     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5969   }
5970   *size = offset;
5971   PetscFunctionReturn(PETSC_SUCCESS);
5972 }
5973 
5974 /*@C
5975   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5976 
5977   Not collective
5978 
5979   Input Parameters:
5980 + dm - The `DM`
5981 . section - The section describing the layout in v, or NULL to use the default section
5982 . v - The local vector
5983 - point - The point in the `DM`
5984 
5985   Input/Output Parameters:
5986 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5987 - values - An array to use for the values, or NULL to have it allocated automatically;
5988            if the user provided NULL, it is a borrowed array and should not be freed
5989 
5990   Level: intermediate
5991 
5992   Notes:
5993   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to NULL in the
5994   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
5995   assembly function, and a user may already have allocated storage for this operation.
5996 
5997   A typical use could be
5998 .vb
5999    values = NULL;
6000    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6001    for (cl = 0; cl < clSize; ++cl) {
6002      <Compute on closure>
6003    }
6004    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6005 .ve
6006   or
6007 .vb
6008    PetscMalloc1(clMaxSize, &values);
6009    for (p = pStart; p < pEnd; ++p) {
6010      clSize = clMaxSize;
6011      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6012      for (cl = 0; cl < clSize; ++cl) {
6013        <Compute on closure>
6014      }
6015    }
6016    PetscFree(values);
6017 .ve
6018 
6019   Fortran Note:
6020   The csize argument is not present in the Fortran binding since it is internal to the array.
6021 
6022 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6023 @*/
6024 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6025 {
6026   PetscSection    clSection;
6027   IS              clPoints;
6028   PetscInt       *points = NULL;
6029   const PetscInt *clp, *perm;
6030   PetscInt        depth, numFields, numPoints, asize;
6031 
6032   PetscFunctionBeginHot;
6033   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6034   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6035   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6036   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6037   PetscCall(DMPlexGetDepth(dm, &depth));
6038   PetscCall(PetscSectionGetNumFields(section, &numFields));
6039   if (depth == 1 && numFields < 2) {
6040     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6041     PetscFunctionReturn(PETSC_SUCCESS);
6042   }
6043   /* Get points */
6044   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6045   /* Get sizes */
6046   asize = 0;
6047   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6048     PetscInt dof;
6049     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6050     asize += dof;
6051   }
6052   if (values) {
6053     const PetscScalar *vArray;
6054     PetscInt           size;
6055 
6056     if (*values) {
6057       PetscCheck(*csize >= asize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Provided array size %" PetscInt_FMT " not sufficient to hold closure size %" PetscInt_FMT, *csize, asize);
6058     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6059     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6060     PetscCall(VecGetArrayRead(v, &vArray));
6061     /* Get values */
6062     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6063     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6064     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6065     /* Cleanup array */
6066     PetscCall(VecRestoreArrayRead(v, &vArray));
6067   }
6068   if (csize) *csize = asize;
6069   /* Cleanup points */
6070   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6071   PetscFunctionReturn(PETSC_SUCCESS);
6072 }
6073 
6074 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6075 {
6076   DMLabel            depthLabel;
6077   PetscSection       clSection;
6078   IS                 clPoints;
6079   PetscScalar       *array;
6080   const PetscScalar *vArray;
6081   PetscInt          *points = NULL;
6082   const PetscInt    *clp, *perm = NULL;
6083   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6084 
6085   PetscFunctionBeginHot;
6086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6087   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6088   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6089   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6090   PetscCall(DMPlexGetDepth(dm, &mdepth));
6091   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6092   PetscCall(PetscSectionGetNumFields(section, &numFields));
6093   if (mdepth == 1 && numFields < 2) {
6094     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6095     PetscFunctionReturn(PETSC_SUCCESS);
6096   }
6097   /* Get points */
6098   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6099   for (clsize = 0, p = 0; p < Np; p++) {
6100     PetscInt dof;
6101     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6102     clsize += dof;
6103   }
6104   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6105   /* Filter points */
6106   for (p = 0; p < numPoints * 2; p += 2) {
6107     PetscInt dep;
6108 
6109     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6110     if (dep != depth) continue;
6111     points[Np * 2 + 0] = points[p];
6112     points[Np * 2 + 1] = points[p + 1];
6113     ++Np;
6114   }
6115   /* Get array */
6116   if (!values || !*values) {
6117     PetscInt asize = 0, dof;
6118 
6119     for (p = 0; p < Np * 2; p += 2) {
6120       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6121       asize += dof;
6122     }
6123     if (!values) {
6124       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6125       if (csize) *csize = asize;
6126       PetscFunctionReturn(PETSC_SUCCESS);
6127     }
6128     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6129   } else {
6130     array = *values;
6131   }
6132   PetscCall(VecGetArrayRead(v, &vArray));
6133   /* Get values */
6134   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6135   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6136   /* Cleanup points */
6137   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6138   /* Cleanup array */
6139   PetscCall(VecRestoreArrayRead(v, &vArray));
6140   if (!*values) {
6141     if (csize) *csize = size;
6142     *values = array;
6143   } else {
6144     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6145     *csize = size;
6146   }
6147   PetscFunctionReturn(PETSC_SUCCESS);
6148 }
6149 
6150 /*@C
6151   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6152 
6153   Not collective
6154 
6155   Input Parameters:
6156 + dm - The `DM`
6157 . section - The section describing the layout in v, or NULL to use the default section
6158 . v - The local vector
6159 . point - The point in the `DM`
6160 . csize - The number of values in the closure, or NULL
6161 - values - The array of values, which is a borrowed array and should not be freed
6162 
6163   Level: intermediate
6164 
6165   Note:
6166   The array values are discarded and not copied back into v. In order to copy values back to v, use `DMPlexVecSetClosure()`
6167 
6168   Fortran Note:
6169   The csize argument is not present in the Fortran binding since it is internal to the array.
6170 
6171 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6172 @*/
6173 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6174 {
6175   PetscInt size = 0;
6176 
6177   PetscFunctionBegin;
6178   /* Should work without recalculating size */
6179   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6180   *values = NULL;
6181   PetscFunctionReturn(PETSC_SUCCESS);
6182 }
6183 
6184 static inline void add(PetscScalar *x, PetscScalar y)
6185 {
6186   *x += y;
6187 }
6188 static inline void insert(PetscScalar *x, PetscScalar y)
6189 {
6190   *x = y;
6191 }
6192 
6193 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[])
6194 {
6195   PetscInt        cdof;  /* The number of constraints on this point */
6196   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6197   PetscScalar    *a;
6198   PetscInt        off, cind = 0, k;
6199 
6200   PetscFunctionBegin;
6201   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6202   PetscCall(PetscSectionGetOffset(section, point, &off));
6203   a = &array[off];
6204   if (!cdof || setBC) {
6205     if (clperm) {
6206       if (perm) {
6207         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6208       } else {
6209         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6210       }
6211     } else {
6212       if (perm) {
6213         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6214       } else {
6215         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6216       }
6217     }
6218   } else {
6219     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6220     if (clperm) {
6221       if (perm) {
6222         for (k = 0; k < dof; ++k) {
6223           if ((cind < cdof) && (k == cdofs[cind])) {
6224             ++cind;
6225             continue;
6226           }
6227           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6228         }
6229       } else {
6230         for (k = 0; k < dof; ++k) {
6231           if ((cind < cdof) && (k == cdofs[cind])) {
6232             ++cind;
6233             continue;
6234           }
6235           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6236         }
6237       }
6238     } else {
6239       if (perm) {
6240         for (k = 0; k < dof; ++k) {
6241           if ((cind < cdof) && (k == cdofs[cind])) {
6242             ++cind;
6243             continue;
6244           }
6245           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6246         }
6247       } else {
6248         for (k = 0; k < dof; ++k) {
6249           if ((cind < cdof) && (k == cdofs[cind])) {
6250             ++cind;
6251             continue;
6252           }
6253           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6254         }
6255       }
6256     }
6257   }
6258   PetscFunctionReturn(PETSC_SUCCESS);
6259 }
6260 
6261 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[])
6262 {
6263   PetscInt        cdof;  /* The number of constraints on this point */
6264   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6265   PetscScalar    *a;
6266   PetscInt        off, cind = 0, k;
6267 
6268   PetscFunctionBegin;
6269   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6270   PetscCall(PetscSectionGetOffset(section, point, &off));
6271   a = &array[off];
6272   if (cdof) {
6273     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6274     if (clperm) {
6275       if (perm) {
6276         for (k = 0; k < dof; ++k) {
6277           if ((cind < cdof) && (k == cdofs[cind])) {
6278             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6279             cind++;
6280           }
6281         }
6282       } else {
6283         for (k = 0; k < dof; ++k) {
6284           if ((cind < cdof) && (k == cdofs[cind])) {
6285             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6286             cind++;
6287           }
6288         }
6289       }
6290     } else {
6291       if (perm) {
6292         for (k = 0; k < dof; ++k) {
6293           if ((cind < cdof) && (k == cdofs[cind])) {
6294             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6295             cind++;
6296           }
6297         }
6298       } else {
6299         for (k = 0; k < dof; ++k) {
6300           if ((cind < cdof) && (k == cdofs[cind])) {
6301             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6302             cind++;
6303           }
6304         }
6305       }
6306     }
6307   }
6308   PetscFunctionReturn(PETSC_SUCCESS);
6309 }
6310 
6311 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[])
6312 {
6313   PetscScalar    *a;
6314   PetscInt        fdof, foff, fcdof, foffset = *offset;
6315   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6316   PetscInt        cind = 0, b;
6317 
6318   PetscFunctionBegin;
6319   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6320   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6321   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6322   a = &array[foff];
6323   if (!fcdof || setBC) {
6324     if (clperm) {
6325       if (perm) {
6326         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6327       } else {
6328         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6329       }
6330     } else {
6331       if (perm) {
6332         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6333       } else {
6334         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6335       }
6336     }
6337   } else {
6338     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6339     if (clperm) {
6340       if (perm) {
6341         for (b = 0; b < fdof; b++) {
6342           if ((cind < fcdof) && (b == fcdofs[cind])) {
6343             ++cind;
6344             continue;
6345           }
6346           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6347         }
6348       } else {
6349         for (b = 0; b < fdof; b++) {
6350           if ((cind < fcdof) && (b == fcdofs[cind])) {
6351             ++cind;
6352             continue;
6353           }
6354           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6355         }
6356       }
6357     } else {
6358       if (perm) {
6359         for (b = 0; b < fdof; b++) {
6360           if ((cind < fcdof) && (b == fcdofs[cind])) {
6361             ++cind;
6362             continue;
6363           }
6364           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6365         }
6366       } else {
6367         for (b = 0; b < fdof; b++) {
6368           if ((cind < fcdof) && (b == fcdofs[cind])) {
6369             ++cind;
6370             continue;
6371           }
6372           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6373         }
6374       }
6375     }
6376   }
6377   *offset += fdof;
6378   PetscFunctionReturn(PETSC_SUCCESS);
6379 }
6380 
6381 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[])
6382 {
6383   PetscScalar    *a;
6384   PetscInt        fdof, foff, fcdof, foffset = *offset;
6385   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6386   PetscInt        Nc, cind = 0, ncind = 0, b;
6387   PetscBool       ncSet, fcSet;
6388 
6389   PetscFunctionBegin;
6390   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6391   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6392   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6393   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6394   a = &array[foff];
6395   if (fcdof) {
6396     /* We just override fcdof and fcdofs with Ncc and comps */
6397     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6398     if (clperm) {
6399       if (perm) {
6400         if (comps) {
6401           for (b = 0; b < fdof; b++) {
6402             ncSet = fcSet = PETSC_FALSE;
6403             if (b % Nc == comps[ncind]) {
6404               ncind = (ncind + 1) % Ncc;
6405               ncSet = PETSC_TRUE;
6406             }
6407             if ((cind < fcdof) && (b == fcdofs[cind])) {
6408               ++cind;
6409               fcSet = PETSC_TRUE;
6410             }
6411             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6412           }
6413         } else {
6414           for (b = 0; b < fdof; b++) {
6415             if ((cind < fcdof) && (b == fcdofs[cind])) {
6416               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6417               ++cind;
6418             }
6419           }
6420         }
6421       } else {
6422         if (comps) {
6423           for (b = 0; b < fdof; b++) {
6424             ncSet = fcSet = PETSC_FALSE;
6425             if (b % Nc == comps[ncind]) {
6426               ncind = (ncind + 1) % Ncc;
6427               ncSet = PETSC_TRUE;
6428             }
6429             if ((cind < fcdof) && (b == fcdofs[cind])) {
6430               ++cind;
6431               fcSet = PETSC_TRUE;
6432             }
6433             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6434           }
6435         } else {
6436           for (b = 0; b < fdof; b++) {
6437             if ((cind < fcdof) && (b == fcdofs[cind])) {
6438               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6439               ++cind;
6440             }
6441           }
6442         }
6443       }
6444     } else {
6445       if (perm) {
6446         if (comps) {
6447           for (b = 0; b < fdof; b++) {
6448             ncSet = fcSet = PETSC_FALSE;
6449             if (b % Nc == comps[ncind]) {
6450               ncind = (ncind + 1) % Ncc;
6451               ncSet = PETSC_TRUE;
6452             }
6453             if ((cind < fcdof) && (b == fcdofs[cind])) {
6454               ++cind;
6455               fcSet = PETSC_TRUE;
6456             }
6457             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6458           }
6459         } else {
6460           for (b = 0; b < fdof; b++) {
6461             if ((cind < fcdof) && (b == fcdofs[cind])) {
6462               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6463               ++cind;
6464             }
6465           }
6466         }
6467       } else {
6468         if (comps) {
6469           for (b = 0; b < fdof; b++) {
6470             ncSet = fcSet = PETSC_FALSE;
6471             if (b % Nc == comps[ncind]) {
6472               ncind = (ncind + 1) % Ncc;
6473               ncSet = PETSC_TRUE;
6474             }
6475             if ((cind < fcdof) && (b == fcdofs[cind])) {
6476               ++cind;
6477               fcSet = PETSC_TRUE;
6478             }
6479             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6480           }
6481         } else {
6482           for (b = 0; b < fdof; b++) {
6483             if ((cind < fcdof) && (b == fcdofs[cind])) {
6484               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6485               ++cind;
6486             }
6487           }
6488         }
6489       }
6490     }
6491   }
6492   *offset += fdof;
6493   PetscFunctionReturn(PETSC_SUCCESS);
6494 }
6495 
6496 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6497 {
6498   PetscScalar    *array;
6499   const PetscInt *cone, *coneO;
6500   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6501 
6502   PetscFunctionBeginHot;
6503   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6504   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6505   PetscCall(DMPlexGetCone(dm, point, &cone));
6506   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6507   PetscCall(VecGetArray(v, &array));
6508   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6509     const PetscInt cp = !p ? point : cone[p - 1];
6510     const PetscInt o  = !p ? 0 : coneO[p - 1];
6511 
6512     if ((cp < pStart) || (cp >= pEnd)) {
6513       dof = 0;
6514       continue;
6515     }
6516     PetscCall(PetscSectionGetDof(section, cp, &dof));
6517     /* ADD_VALUES */
6518     {
6519       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6520       PetscScalar    *a;
6521       PetscInt        cdof, coff, cind = 0, k;
6522 
6523       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6524       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6525       a = &array[coff];
6526       if (!cdof) {
6527         if (o >= 0) {
6528           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6529         } else {
6530           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6531         }
6532       } else {
6533         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6534         if (o >= 0) {
6535           for (k = 0; k < dof; ++k) {
6536             if ((cind < cdof) && (k == cdofs[cind])) {
6537               ++cind;
6538               continue;
6539             }
6540             a[k] += values[off + k];
6541           }
6542         } else {
6543           for (k = 0; k < dof; ++k) {
6544             if ((cind < cdof) && (k == cdofs[cind])) {
6545               ++cind;
6546               continue;
6547             }
6548             a[k] += values[off + dof - k - 1];
6549           }
6550         }
6551       }
6552     }
6553   }
6554   PetscCall(VecRestoreArray(v, &array));
6555   PetscFunctionReturn(PETSC_SUCCESS);
6556 }
6557 
6558 /*@C
6559   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6560 
6561   Not collective
6562 
6563   Input Parameters:
6564 + dm - The `DM`
6565 . section - The section describing the layout in v, or NULL to use the default section
6566 . v - The local vector
6567 . point - The point in the DM
6568 . values - The array of values
6569 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6570          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6571 
6572   Level: intermediate
6573 
6574 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6575 @*/
6576 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6577 {
6578   PetscSection    clSection;
6579   IS              clPoints;
6580   PetscScalar    *array;
6581   PetscInt       *points = NULL;
6582   const PetscInt *clp, *clperm = NULL;
6583   PetscInt        depth, numFields, numPoints, p, clsize;
6584 
6585   PetscFunctionBeginHot;
6586   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6587   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6588   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6589   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6590   PetscCall(DMPlexGetDepth(dm, &depth));
6591   PetscCall(PetscSectionGetNumFields(section, &numFields));
6592   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6593     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6594     PetscFunctionReturn(PETSC_SUCCESS);
6595   }
6596   /* Get points */
6597   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6598   for (clsize = 0, p = 0; p < numPoints; p++) {
6599     PetscInt dof;
6600     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6601     clsize += dof;
6602   }
6603   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6604   /* Get array */
6605   PetscCall(VecGetArray(v, &array));
6606   /* Get values */
6607   if (numFields > 0) {
6608     PetscInt offset = 0, f;
6609     for (f = 0; f < numFields; ++f) {
6610       const PetscInt    **perms = NULL;
6611       const PetscScalar **flips = NULL;
6612 
6613       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6614       switch (mode) {
6615       case INSERT_VALUES:
6616         for (p = 0; p < numPoints; p++) {
6617           const PetscInt     point = points[2 * p];
6618           const PetscInt    *perm  = perms ? perms[p] : NULL;
6619           const PetscScalar *flip  = flips ? flips[p] : NULL;
6620           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6621         }
6622         break;
6623       case INSERT_ALL_VALUES:
6624         for (p = 0; p < numPoints; p++) {
6625           const PetscInt     point = points[2 * p];
6626           const PetscInt    *perm  = perms ? perms[p] : NULL;
6627           const PetscScalar *flip  = flips ? flips[p] : NULL;
6628           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6629         }
6630         break;
6631       case INSERT_BC_VALUES:
6632         for (p = 0; p < numPoints; p++) {
6633           const PetscInt     point = points[2 * p];
6634           const PetscInt    *perm  = perms ? perms[p] : NULL;
6635           const PetscScalar *flip  = flips ? flips[p] : NULL;
6636           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6637         }
6638         break;
6639       case ADD_VALUES:
6640         for (p = 0; p < numPoints; p++) {
6641           const PetscInt     point = points[2 * p];
6642           const PetscInt    *perm  = perms ? perms[p] : NULL;
6643           const PetscScalar *flip  = flips ? flips[p] : NULL;
6644           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6645         }
6646         break;
6647       case ADD_ALL_VALUES:
6648         for (p = 0; p < numPoints; p++) {
6649           const PetscInt     point = points[2 * p];
6650           const PetscInt    *perm  = perms ? perms[p] : NULL;
6651           const PetscScalar *flip  = flips ? flips[p] : NULL;
6652           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6653         }
6654         break;
6655       case ADD_BC_VALUES:
6656         for (p = 0; p < numPoints; p++) {
6657           const PetscInt     point = points[2 * p];
6658           const PetscInt    *perm  = perms ? perms[p] : NULL;
6659           const PetscScalar *flip  = flips ? flips[p] : NULL;
6660           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6661         }
6662         break;
6663       default:
6664         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6665       }
6666       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6667     }
6668   } else {
6669     PetscInt            dof, off;
6670     const PetscInt    **perms = NULL;
6671     const PetscScalar **flips = NULL;
6672 
6673     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6674     switch (mode) {
6675     case INSERT_VALUES:
6676       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6677         const PetscInt     point = points[2 * p];
6678         const PetscInt    *perm  = perms ? perms[p] : NULL;
6679         const PetscScalar *flip  = flips ? flips[p] : NULL;
6680         PetscCall(PetscSectionGetDof(section, point, &dof));
6681         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6682       }
6683       break;
6684     case INSERT_ALL_VALUES:
6685       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6686         const PetscInt     point = points[2 * p];
6687         const PetscInt    *perm  = perms ? perms[p] : NULL;
6688         const PetscScalar *flip  = flips ? flips[p] : NULL;
6689         PetscCall(PetscSectionGetDof(section, point, &dof));
6690         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6691       }
6692       break;
6693     case INSERT_BC_VALUES:
6694       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6695         const PetscInt     point = points[2 * p];
6696         const PetscInt    *perm  = perms ? perms[p] : NULL;
6697         const PetscScalar *flip  = flips ? flips[p] : NULL;
6698         PetscCall(PetscSectionGetDof(section, point, &dof));
6699         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6700       }
6701       break;
6702     case ADD_VALUES:
6703       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6704         const PetscInt     point = points[2 * p];
6705         const PetscInt    *perm  = perms ? perms[p] : NULL;
6706         const PetscScalar *flip  = flips ? flips[p] : NULL;
6707         PetscCall(PetscSectionGetDof(section, point, &dof));
6708         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6709       }
6710       break;
6711     case ADD_ALL_VALUES:
6712       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6713         const PetscInt     point = points[2 * p];
6714         const PetscInt    *perm  = perms ? perms[p] : NULL;
6715         const PetscScalar *flip  = flips ? flips[p] : NULL;
6716         PetscCall(PetscSectionGetDof(section, point, &dof));
6717         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6718       }
6719       break;
6720     case ADD_BC_VALUES:
6721       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6722         const PetscInt     point = points[2 * p];
6723         const PetscInt    *perm  = perms ? perms[p] : NULL;
6724         const PetscScalar *flip  = flips ? flips[p] : NULL;
6725         PetscCall(PetscSectionGetDof(section, point, &dof));
6726         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6727       }
6728       break;
6729     default:
6730       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6731     }
6732     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6733   }
6734   /* Cleanup points */
6735   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6736   /* Cleanup array */
6737   PetscCall(VecRestoreArray(v, &array));
6738   PetscFunctionReturn(PETSC_SUCCESS);
6739 }
6740 
6741 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6742 {
6743   const PetscInt *supp, *cone;
6744   PetscScalar    *a;
6745   PetscInt        dim, Ns, dof, off, n = 0;
6746 
6747   PetscFunctionBegin;
6748   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6749   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6750   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6751   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6752   if (PetscDefined(USE_DEBUG)) {
6753     PetscInt vStart, vEnd;
6754 
6755     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6756     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);
6757   }
6758   PetscValidScalarPointer(values, 5);
6759 
6760   PetscCall(DMGetDimension(dm, &dim));
6761   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6762   PetscCall(DMPlexGetSupport(dm, point, &supp));
6763   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);
6764   PetscCall(VecGetArray(v, &a));
6765   PetscCall(PetscSectionGetDof(section, point, &dof));
6766   PetscCall(PetscSectionGetOffset(section, point, &off));
6767   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6768   for (PetscInt d = 0; d < dim; ++d) {
6769     // Left edge
6770     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6771     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6772     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6773     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6774     // Right edge
6775     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6776     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6777     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6778     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6779   }
6780   PetscCall(VecRestoreArray(v, &a));
6781   PetscFunctionReturn(PETSC_SUCCESS);
6782 }
6783 
6784 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6785 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6786 {
6787   PetscFunctionBegin;
6788   *contains = PETSC_TRUE;
6789   if (label) {
6790     PetscInt fdof;
6791 
6792     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6793     if (!*contains) {
6794       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6795       *offset += fdof;
6796       PetscFunctionReturn(PETSC_SUCCESS);
6797     }
6798   }
6799   PetscFunctionReturn(PETSC_SUCCESS);
6800 }
6801 
6802 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6803 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)
6804 {
6805   PetscSection    clSection;
6806   IS              clPoints;
6807   PetscScalar    *array;
6808   PetscInt       *points = NULL;
6809   const PetscInt *clp;
6810   PetscInt        numFields, numPoints, p;
6811   PetscInt        offset = 0, f;
6812 
6813   PetscFunctionBeginHot;
6814   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6815   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6816   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6817   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6818   PetscCall(PetscSectionGetNumFields(section, &numFields));
6819   /* Get points */
6820   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6821   /* Get array */
6822   PetscCall(VecGetArray(v, &array));
6823   /* Get values */
6824   for (f = 0; f < numFields; ++f) {
6825     const PetscInt    **perms = NULL;
6826     const PetscScalar **flips = NULL;
6827     PetscBool           contains;
6828 
6829     if (!fieldActive[f]) {
6830       for (p = 0; p < numPoints * 2; p += 2) {
6831         PetscInt fdof;
6832         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6833         offset += fdof;
6834       }
6835       continue;
6836     }
6837     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6838     switch (mode) {
6839     case INSERT_VALUES:
6840       for (p = 0; p < numPoints; p++) {
6841         const PetscInt     point = points[2 * p];
6842         const PetscInt    *perm  = perms ? perms[p] : NULL;
6843         const PetscScalar *flip  = flips ? flips[p] : NULL;
6844         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6845         if (!contains) continue;
6846         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6847       }
6848       break;
6849     case INSERT_ALL_VALUES:
6850       for (p = 0; p < numPoints; p++) {
6851         const PetscInt     point = points[2 * p];
6852         const PetscInt    *perm  = perms ? perms[p] : NULL;
6853         const PetscScalar *flip  = flips ? flips[p] : NULL;
6854         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6855         if (!contains) continue;
6856         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6857       }
6858       break;
6859     case INSERT_BC_VALUES:
6860       for (p = 0; p < numPoints; p++) {
6861         const PetscInt     point = points[2 * p];
6862         const PetscInt    *perm  = perms ? perms[p] : NULL;
6863         const PetscScalar *flip  = flips ? flips[p] : NULL;
6864         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6865         if (!contains) continue;
6866         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6867       }
6868       break;
6869     case ADD_VALUES:
6870       for (p = 0; p < numPoints; p++) {
6871         const PetscInt     point = points[2 * p];
6872         const PetscInt    *perm  = perms ? perms[p] : NULL;
6873         const PetscScalar *flip  = flips ? flips[p] : NULL;
6874         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6875         if (!contains) continue;
6876         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6877       }
6878       break;
6879     case ADD_ALL_VALUES:
6880       for (p = 0; p < numPoints; p++) {
6881         const PetscInt     point = points[2 * p];
6882         const PetscInt    *perm  = perms ? perms[p] : NULL;
6883         const PetscScalar *flip  = flips ? flips[p] : NULL;
6884         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6885         if (!contains) continue;
6886         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6887       }
6888       break;
6889     default:
6890       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6891     }
6892     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6893   }
6894   /* Cleanup points */
6895   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6896   /* Cleanup array */
6897   PetscCall(VecRestoreArray(v, &array));
6898   PetscFunctionReturn(PETSC_SUCCESS);
6899 }
6900 
6901 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6902 {
6903   PetscMPIInt rank;
6904   PetscInt    i, j;
6905 
6906   PetscFunctionBegin;
6907   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6908   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6909   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6910   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6911   numCIndices = numCIndices ? numCIndices : numRIndices;
6912   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6913   for (i = 0; i < numRIndices; i++) {
6914     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6915     for (j = 0; j < numCIndices; j++) {
6916 #if defined(PETSC_USE_COMPLEX)
6917       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6918 #else
6919       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6920 #endif
6921     }
6922     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6923   }
6924   PetscFunctionReturn(PETSC_SUCCESS);
6925 }
6926 
6927 /*
6928   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6929 
6930   Input Parameters:
6931 + section - The section for this data layout
6932 . islocal - Is the section (and thus indices being requested) local or global?
6933 . point   - The point contributing dofs with these indices
6934 . off     - The global offset of this point
6935 . loff    - The local offset of each field
6936 . setBC   - The flag determining whether to include indices of boundary values
6937 . perm    - A permutation of the dofs on this point, or NULL
6938 - indperm - A permutation of the entire indices array, or NULL
6939 
6940   Output Parameter:
6941 . indices - Indices for dofs on this point
6942 
6943   Level: developer
6944 
6945   Note: The indices could be local or global, depending on the value of 'off'.
6946 */
6947 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6948 {
6949   PetscInt        dof;   /* The number of unknowns on this point */
6950   PetscInt        cdof;  /* The number of constraints on this point */
6951   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6952   PetscInt        cind = 0, k;
6953 
6954   PetscFunctionBegin;
6955   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6956   PetscCall(PetscSectionGetDof(section, point, &dof));
6957   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6958   if (!cdof || setBC) {
6959     for (k = 0; k < dof; ++k) {
6960       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6961       const PetscInt ind    = indperm ? indperm[preind] : preind;
6962 
6963       indices[ind] = off + k;
6964     }
6965   } else {
6966     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6967     for (k = 0; k < dof; ++k) {
6968       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6969       const PetscInt ind    = indperm ? indperm[preind] : preind;
6970 
6971       if ((cind < cdof) && (k == cdofs[cind])) {
6972         /* Insert check for returning constrained indices */
6973         indices[ind] = -(off + k + 1);
6974         ++cind;
6975       } else {
6976         indices[ind] = off + k - (islocal ? 0 : cind);
6977       }
6978     }
6979   }
6980   *loff += dof;
6981   PetscFunctionReturn(PETSC_SUCCESS);
6982 }
6983 
6984 /*
6985  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6986 
6987  Input Parameters:
6988 + section - a section (global or local)
6989 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6990 . point - point within section
6991 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6992 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6993 . setBC - identify constrained (boundary condition) points via involution.
6994 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6995 . permsoff - offset
6996 - indperm - index permutation
6997 
6998  Output Parameter:
6999 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7000 . indices - array to hold indices (as defined by section) of each dof associated with point
7001 
7002  Notes:
7003  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7004  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7005  in the local vector.
7006 
7007  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7008  significant).  It is invalid to call with a global section and setBC=true.
7009 
7010  Developer Note:
7011  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7012  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7013  offset could be obtained from the section instead of passing it explicitly as we do now.
7014 
7015  Example:
7016  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7017  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7018  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7019  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.
7020 
7021  Level: developer
7022 */
7023 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[])
7024 {
7025   PetscInt numFields, foff, f;
7026 
7027   PetscFunctionBegin;
7028   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7029   PetscCall(PetscSectionGetNumFields(section, &numFields));
7030   for (f = 0, foff = 0; f < numFields; ++f) {
7031     PetscInt        fdof, cfdof;
7032     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7033     PetscInt        cind = 0, b;
7034     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7035 
7036     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7037     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7038     if (!cfdof || setBC) {
7039       for (b = 0; b < fdof; ++b) {
7040         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7041         const PetscInt ind    = indperm ? indperm[preind] : preind;
7042 
7043         indices[ind] = off + foff + b;
7044       }
7045     } else {
7046       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7047       for (b = 0; b < fdof; ++b) {
7048         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7049         const PetscInt ind    = indperm ? indperm[preind] : preind;
7050 
7051         if ((cind < cfdof) && (b == fcdofs[cind])) {
7052           indices[ind] = -(off + foff + b + 1);
7053           ++cind;
7054         } else {
7055           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7056         }
7057       }
7058     }
7059     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7060     foffs[f] += fdof;
7061   }
7062   PetscFunctionReturn(PETSC_SUCCESS);
7063 }
7064 
7065 /*
7066   This version believes the globalSection offsets for each field, rather than just the point offset
7067 
7068  . foffs - The offset into 'indices' for each field, since it is segregated by field
7069 
7070  Notes:
7071  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7072  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7073 */
7074 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7075 {
7076   PetscInt numFields, foff, f;
7077 
7078   PetscFunctionBegin;
7079   PetscCall(PetscSectionGetNumFields(section, &numFields));
7080   for (f = 0; f < numFields; ++f) {
7081     PetscInt        fdof, cfdof;
7082     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7083     PetscInt        cind = 0, b;
7084     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7085 
7086     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7087     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7088     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7089     if (!cfdof) {
7090       for (b = 0; b < fdof; ++b) {
7091         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7092         const PetscInt ind    = indperm ? indperm[preind] : preind;
7093 
7094         indices[ind] = foff + b;
7095       }
7096     } else {
7097       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7098       for (b = 0; b < fdof; ++b) {
7099         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7100         const PetscInt ind    = indperm ? indperm[preind] : preind;
7101 
7102         if ((cind < cfdof) && (b == fcdofs[cind])) {
7103           indices[ind] = -(foff + b + 1);
7104           ++cind;
7105         } else {
7106           indices[ind] = foff + b - cind;
7107         }
7108       }
7109     }
7110     foffs[f] += fdof;
7111   }
7112   PetscFunctionReturn(PETSC_SUCCESS);
7113 }
7114 
7115 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)
7116 {
7117   Mat             cMat;
7118   PetscSection    aSec, cSec;
7119   IS              aIS;
7120   PetscInt        aStart = -1, aEnd = -1;
7121   const PetscInt *anchors;
7122   PetscInt        numFields, f, p, q, newP = 0;
7123   PetscInt        newNumPoints = 0, newNumIndices = 0;
7124   PetscInt       *newPoints, *indices, *newIndices;
7125   PetscInt        maxAnchor, maxDof;
7126   PetscInt        newOffsets[32];
7127   PetscInt       *pointMatOffsets[32];
7128   PetscInt       *newPointOffsets[32];
7129   PetscScalar    *pointMat[32];
7130   PetscScalar    *newValues      = NULL, *tmpValues;
7131   PetscBool       anyConstrained = PETSC_FALSE;
7132 
7133   PetscFunctionBegin;
7134   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7135   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7136   PetscCall(PetscSectionGetNumFields(section, &numFields));
7137 
7138   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7139   /* if there are point-to-point constraints */
7140   if (aSec) {
7141     PetscCall(PetscArrayzero(newOffsets, 32));
7142     PetscCall(ISGetIndices(aIS, &anchors));
7143     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7144     /* figure out how many points are going to be in the new element matrix
7145      * (we allow double counting, because it's all just going to be summed
7146      * into the global matrix anyway) */
7147     for (p = 0; p < 2 * numPoints; p += 2) {
7148       PetscInt b    = points[p];
7149       PetscInt bDof = 0, bSecDof;
7150 
7151       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7152       if (!bSecDof) continue;
7153       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7154       if (bDof) {
7155         /* this point is constrained */
7156         /* it is going to be replaced by its anchors */
7157         PetscInt bOff, q;
7158 
7159         anyConstrained = PETSC_TRUE;
7160         newNumPoints += bDof;
7161         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7162         for (q = 0; q < bDof; q++) {
7163           PetscInt a = anchors[bOff + q];
7164           PetscInt aDof;
7165 
7166           PetscCall(PetscSectionGetDof(section, a, &aDof));
7167           newNumIndices += aDof;
7168           for (f = 0; f < numFields; ++f) {
7169             PetscInt fDof;
7170 
7171             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7172             newOffsets[f + 1] += fDof;
7173           }
7174         }
7175       } else {
7176         /* this point is not constrained */
7177         newNumPoints++;
7178         newNumIndices += bSecDof;
7179         for (f = 0; f < numFields; ++f) {
7180           PetscInt fDof;
7181 
7182           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7183           newOffsets[f + 1] += fDof;
7184         }
7185       }
7186     }
7187   }
7188   if (!anyConstrained) {
7189     if (outNumPoints) *outNumPoints = 0;
7190     if (outNumIndices) *outNumIndices = 0;
7191     if (outPoints) *outPoints = NULL;
7192     if (outValues) *outValues = NULL;
7193     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7194     PetscFunctionReturn(PETSC_SUCCESS);
7195   }
7196 
7197   if (outNumPoints) *outNumPoints = newNumPoints;
7198   if (outNumIndices) *outNumIndices = newNumIndices;
7199 
7200   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7201 
7202   if (!outPoints && !outValues) {
7203     if (offsets) {
7204       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7205     }
7206     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7207     PetscFunctionReturn(PETSC_SUCCESS);
7208   }
7209 
7210   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7211 
7212   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7213 
7214   /* workspaces */
7215   if (numFields) {
7216     for (f = 0; f < numFields; f++) {
7217       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7218       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7219     }
7220   } else {
7221     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7222     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7223   }
7224 
7225   /* get workspaces for the point-to-point matrices */
7226   if (numFields) {
7227     PetscInt totalOffset, totalMatOffset;
7228 
7229     for (p = 0; p < numPoints; p++) {
7230       PetscInt b    = points[2 * p];
7231       PetscInt bDof = 0, bSecDof;
7232 
7233       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7234       if (!bSecDof) {
7235         for (f = 0; f < numFields; f++) {
7236           newPointOffsets[f][p + 1] = 0;
7237           pointMatOffsets[f][p + 1] = 0;
7238         }
7239         continue;
7240       }
7241       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7242       if (bDof) {
7243         for (f = 0; f < numFields; f++) {
7244           PetscInt fDof, q, bOff, allFDof = 0;
7245 
7246           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7247           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7248           for (q = 0; q < bDof; q++) {
7249             PetscInt a = anchors[bOff + q];
7250             PetscInt aFDof;
7251 
7252             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7253             allFDof += aFDof;
7254           }
7255           newPointOffsets[f][p + 1] = allFDof;
7256           pointMatOffsets[f][p + 1] = fDof * allFDof;
7257         }
7258       } else {
7259         for (f = 0; f < numFields; f++) {
7260           PetscInt fDof;
7261 
7262           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7263           newPointOffsets[f][p + 1] = fDof;
7264           pointMatOffsets[f][p + 1] = 0;
7265         }
7266       }
7267     }
7268     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7269       newPointOffsets[f][0] = totalOffset;
7270       pointMatOffsets[f][0] = totalMatOffset;
7271       for (p = 0; p < numPoints; p++) {
7272         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7273         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7274       }
7275       totalOffset    = newPointOffsets[f][numPoints];
7276       totalMatOffset = pointMatOffsets[f][numPoints];
7277       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7278     }
7279   } else {
7280     for (p = 0; p < numPoints; p++) {
7281       PetscInt b    = points[2 * p];
7282       PetscInt bDof = 0, bSecDof;
7283 
7284       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7285       if (!bSecDof) {
7286         newPointOffsets[0][p + 1] = 0;
7287         pointMatOffsets[0][p + 1] = 0;
7288         continue;
7289       }
7290       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7291       if (bDof) {
7292         PetscInt bOff, q, allDof = 0;
7293 
7294         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7295         for (q = 0; q < bDof; q++) {
7296           PetscInt a = anchors[bOff + q], aDof;
7297 
7298           PetscCall(PetscSectionGetDof(section, a, &aDof));
7299           allDof += aDof;
7300         }
7301         newPointOffsets[0][p + 1] = allDof;
7302         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7303       } else {
7304         newPointOffsets[0][p + 1] = bSecDof;
7305         pointMatOffsets[0][p + 1] = 0;
7306       }
7307     }
7308     newPointOffsets[0][0] = 0;
7309     pointMatOffsets[0][0] = 0;
7310     for (p = 0; p < numPoints; p++) {
7311       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7312       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7313     }
7314     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7315   }
7316 
7317   /* output arrays */
7318   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7319 
7320   /* get the point-to-point matrices; construct newPoints */
7321   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7322   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7323   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7324   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7325   if (numFields) {
7326     for (p = 0, newP = 0; p < numPoints; p++) {
7327       PetscInt b    = points[2 * p];
7328       PetscInt o    = points[2 * p + 1];
7329       PetscInt bDof = 0, bSecDof;
7330 
7331       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7332       if (!bSecDof) continue;
7333       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7334       if (bDof) {
7335         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7336 
7337         fStart[0] = 0;
7338         fEnd[0]   = 0;
7339         for (f = 0; f < numFields; f++) {
7340           PetscInt fDof;
7341 
7342           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7343           fStart[f + 1] = fStart[f] + fDof;
7344           fEnd[f + 1]   = fStart[f + 1];
7345         }
7346         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7347         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7348 
7349         fAnchorStart[0] = 0;
7350         fAnchorEnd[0]   = 0;
7351         for (f = 0; f < numFields; f++) {
7352           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7353 
7354           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7355           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7356         }
7357         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7358         for (q = 0; q < bDof; q++) {
7359           PetscInt a = anchors[bOff + q], aOff;
7360 
7361           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7362           newPoints[2 * (newP + q)]     = a;
7363           newPoints[2 * (newP + q) + 1] = 0;
7364           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7365           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7366         }
7367         newP += bDof;
7368 
7369         if (outValues) {
7370           /* get the point-to-point submatrix */
7371           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]));
7372         }
7373       } else {
7374         newPoints[2 * newP]     = b;
7375         newPoints[2 * newP + 1] = o;
7376         newP++;
7377       }
7378     }
7379   } else {
7380     for (p = 0; p < numPoints; p++) {
7381       PetscInt b    = points[2 * p];
7382       PetscInt o    = points[2 * p + 1];
7383       PetscInt bDof = 0, bSecDof;
7384 
7385       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7386       if (!bSecDof) continue;
7387       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7388       if (bDof) {
7389         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7390 
7391         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7392         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7393 
7394         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7395         for (q = 0; q < bDof; q++) {
7396           PetscInt a = anchors[bOff + q], aOff;
7397 
7398           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7399 
7400           newPoints[2 * (newP + q)]     = a;
7401           newPoints[2 * (newP + q) + 1] = 0;
7402           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7403           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7404         }
7405         newP += bDof;
7406 
7407         /* get the point-to-point submatrix */
7408         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7409       } else {
7410         newPoints[2 * newP]     = b;
7411         newPoints[2 * newP + 1] = o;
7412         newP++;
7413       }
7414     }
7415   }
7416 
7417   if (outValues) {
7418     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7419     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7420     /* multiply constraints on the right */
7421     if (numFields) {
7422       for (f = 0; f < numFields; f++) {
7423         PetscInt oldOff = offsets[f];
7424 
7425         for (p = 0; p < numPoints; p++) {
7426           PetscInt cStart = newPointOffsets[f][p];
7427           PetscInt b      = points[2 * p];
7428           PetscInt c, r, k;
7429           PetscInt dof;
7430 
7431           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7432           if (!dof) continue;
7433           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7434             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7435             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7436 
7437             for (r = 0; r < numIndices; r++) {
7438               for (c = 0; c < nCols; c++) {
7439                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7440               }
7441             }
7442           } else {
7443             /* copy this column as is */
7444             for (r = 0; r < numIndices; r++) {
7445               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7446             }
7447           }
7448           oldOff += dof;
7449         }
7450       }
7451     } else {
7452       PetscInt oldOff = 0;
7453       for (p = 0; p < numPoints; p++) {
7454         PetscInt cStart = newPointOffsets[0][p];
7455         PetscInt b      = points[2 * p];
7456         PetscInt c, r, k;
7457         PetscInt dof;
7458 
7459         PetscCall(PetscSectionGetDof(section, b, &dof));
7460         if (!dof) continue;
7461         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7462           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7463           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7464 
7465           for (r = 0; r < numIndices; r++) {
7466             for (c = 0; c < nCols; c++) {
7467               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7468             }
7469           }
7470         } else {
7471           /* copy this column as is */
7472           for (r = 0; r < numIndices; r++) {
7473             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7474           }
7475         }
7476         oldOff += dof;
7477       }
7478     }
7479 
7480     if (multiplyLeft) {
7481       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7482       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7483       /* multiply constraints transpose on the left */
7484       if (numFields) {
7485         for (f = 0; f < numFields; f++) {
7486           PetscInt oldOff = offsets[f];
7487 
7488           for (p = 0; p < numPoints; p++) {
7489             PetscInt rStart = newPointOffsets[f][p];
7490             PetscInt b      = points[2 * p];
7491             PetscInt c, r, k;
7492             PetscInt dof;
7493 
7494             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7495             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7496               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7497               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7498 
7499               for (r = 0; r < nRows; r++) {
7500                 for (c = 0; c < newNumIndices; c++) {
7501                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7502                 }
7503               }
7504             } else {
7505               /* copy this row as is */
7506               for (r = 0; r < dof; r++) {
7507                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7508               }
7509             }
7510             oldOff += dof;
7511           }
7512         }
7513       } else {
7514         PetscInt oldOff = 0;
7515 
7516         for (p = 0; p < numPoints; p++) {
7517           PetscInt rStart = newPointOffsets[0][p];
7518           PetscInt b      = points[2 * p];
7519           PetscInt c, r, k;
7520           PetscInt dof;
7521 
7522           PetscCall(PetscSectionGetDof(section, b, &dof));
7523           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7524             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7525             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7526 
7527             for (r = 0; r < nRows; r++) {
7528               for (c = 0; c < newNumIndices; c++) {
7529                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7530               }
7531             }
7532           } else {
7533             /* copy this row as is */
7534             for (r = 0; r < dof; r++) {
7535               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7536             }
7537           }
7538           oldOff += dof;
7539         }
7540       }
7541 
7542       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7543     } else {
7544       newValues = tmpValues;
7545     }
7546   }
7547 
7548   /* clean up */
7549   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7550   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7551 
7552   if (numFields) {
7553     for (f = 0; f < numFields; f++) {
7554       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7555       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7556       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7557     }
7558   } else {
7559     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7560     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7561     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7562   }
7563   PetscCall(ISRestoreIndices(aIS, &anchors));
7564 
7565   /* output */
7566   if (outPoints) {
7567     *outPoints = newPoints;
7568   } else {
7569     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7570   }
7571   if (outValues) *outValues = newValues;
7572   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7573   PetscFunctionReturn(PETSC_SUCCESS);
7574 }
7575 
7576 /*@C
7577   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7578 
7579   Not collective
7580 
7581   Input Parameters:
7582 + dm         - The `DM`
7583 . section    - The `PetscSection` describing the points (a local section)
7584 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7585 . point      - The point defining the closure
7586 - useClPerm  - Use the closure point permutation if available
7587 
7588   Output Parameters:
7589 + numIndices - The number of dof indices in the closure of point with the input sections
7590 . indices    - The dof indices
7591 . outOffsets - Array to write the field offsets into, or NULL
7592 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7593 
7594   Level: advanced
7595 
7596   Notes:
7597   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7598 
7599   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7600   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7601   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7602   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7603   indices (with the above semantics) are implied.
7604 
7605 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7606           `PetscSection`, `DMGetGlobalSection()`
7607 @*/
7608 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7609 {
7610   /* Closure ordering */
7611   PetscSection    clSection;
7612   IS              clPoints;
7613   const PetscInt *clp;
7614   PetscInt       *points;
7615   const PetscInt *clperm = NULL;
7616   /* Dof permutation and sign flips */
7617   const PetscInt    **perms[32] = {NULL};
7618   const PetscScalar **flips[32] = {NULL};
7619   PetscScalar        *valCopy   = NULL;
7620   /* Hanging node constraints */
7621   PetscInt    *pointsC = NULL;
7622   PetscScalar *valuesC = NULL;
7623   PetscInt     NclC, NiC;
7624 
7625   PetscInt *idx;
7626   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7627   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7628 
7629   PetscFunctionBeginHot;
7630   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7631   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7632   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7633   if (numIndices) PetscValidIntPointer(numIndices, 6);
7634   if (indices) PetscValidPointer(indices, 7);
7635   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7636   if (values) PetscValidPointer(values, 9);
7637   PetscCall(PetscSectionGetNumFields(section, &Nf));
7638   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7639   PetscCall(PetscArrayzero(offsets, 32));
7640   /* 1) Get points in closure */
7641   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7642   if (useClPerm) {
7643     PetscInt depth, clsize;
7644     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7645     for (clsize = 0, p = 0; p < Ncl; p++) {
7646       PetscInt dof;
7647       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7648       clsize += dof;
7649     }
7650     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7651   }
7652   /* 2) Get number of indices on these points and field offsets from section */
7653   for (p = 0; p < Ncl * 2; p += 2) {
7654     PetscInt dof, fdof;
7655 
7656     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7657     for (f = 0; f < Nf; ++f) {
7658       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7659       offsets[f + 1] += fdof;
7660     }
7661     Ni += dof;
7662   }
7663   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7664   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7665   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7666   for (f = 0; f < PetscMax(1, Nf); ++f) {
7667     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7668     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7669     /* may need to apply sign changes to the element matrix */
7670     if (values && flips[f]) {
7671       PetscInt foffset = offsets[f];
7672 
7673       for (p = 0; p < Ncl; ++p) {
7674         PetscInt           pnt  = points[2 * p], fdof;
7675         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7676 
7677         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7678         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7679         if (flip) {
7680           PetscInt i, j, k;
7681 
7682           if (!valCopy) {
7683             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7684             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7685             *values = valCopy;
7686           }
7687           for (i = 0; i < fdof; ++i) {
7688             PetscScalar fval = flip[i];
7689 
7690             for (k = 0; k < Ni; ++k) {
7691               valCopy[Ni * (foffset + i) + k] *= fval;
7692               valCopy[Ni * k + (foffset + i)] *= fval;
7693             }
7694           }
7695         }
7696         foffset += fdof;
7697       }
7698     }
7699   }
7700   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7701   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7702   if (NclC) {
7703     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7704     for (f = 0; f < PetscMax(1, Nf); ++f) {
7705       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7706       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7707     }
7708     for (f = 0; f < PetscMax(1, Nf); ++f) {
7709       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7710       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7711     }
7712     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7713     Ncl    = NclC;
7714     Ni     = NiC;
7715     points = pointsC;
7716     if (values) *values = valuesC;
7717   }
7718   /* 5) Calculate indices */
7719   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7720   if (Nf) {
7721     PetscInt  idxOff;
7722     PetscBool useFieldOffsets;
7723 
7724     if (outOffsets) {
7725       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7726     }
7727     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7728     if (useFieldOffsets) {
7729       for (p = 0; p < Ncl; ++p) {
7730         const PetscInt pnt = points[p * 2];
7731 
7732         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7733       }
7734     } else {
7735       for (p = 0; p < Ncl; ++p) {
7736         const PetscInt pnt = points[p * 2];
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
7741          * global section. */
7742         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7743       }
7744     }
7745   } else {
7746     PetscInt off = 0, idxOff;
7747 
7748     for (p = 0; p < Ncl; ++p) {
7749       const PetscInt  pnt  = points[p * 2];
7750       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7751 
7752       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7753       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7754        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7755       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7756     }
7757   }
7758   /* 6) Cleanup */
7759   for (f = 0; f < PetscMax(1, Nf); ++f) {
7760     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7761     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7762   }
7763   if (NclC) {
7764     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7765   } else {
7766     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7767   }
7768 
7769   if (numIndices) *numIndices = Ni;
7770   if (indices) *indices = idx;
7771   PetscFunctionReturn(PETSC_SUCCESS);
7772 }
7773 
7774 /*@C
7775   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7776 
7777   Not collective
7778 
7779   Input Parameters:
7780 + dm         - The `DM`
7781 . section    - The `PetscSection` describing the points (a local section)
7782 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7783 . point      - The point defining the closure
7784 - useClPerm  - Use the closure point permutation if available
7785 
7786   Output Parameters:
7787 + numIndices - The number of dof indices in the closure of point with the input sections
7788 . indices    - The dof indices
7789 . outOffsets - Array to write the field offsets into, or NULL
7790 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7791 
7792   Level: advanced
7793 
7794   Notes:
7795   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7796 
7797   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7798   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7799   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7800   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7801   indices (with the above semantics) are implied.
7802 
7803 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7804 @*/
7805 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7806 {
7807   PetscFunctionBegin;
7808   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7809   PetscValidPointer(indices, 7);
7810   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7811   PetscFunctionReturn(PETSC_SUCCESS);
7812 }
7813 
7814 /*@C
7815   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7816 
7817   Not collective
7818 
7819   Input Parameters:
7820 + dm - The `DM`
7821 . section - The section describing the layout in v, or NULL to use the default section
7822 . globalSection - The section describing the layout in v, or NULL to use the default global section
7823 . A - The matrix
7824 . point - The point in the `DM`
7825 . values - The array of values
7826 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7827 
7828   Level: intermediate
7829 
7830 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7831 @*/
7832 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7833 {
7834   DM_Plex           *mesh = (DM_Plex *)dm->data;
7835   PetscInt          *indices;
7836   PetscInt           numIndices;
7837   const PetscScalar *valuesOrig = values;
7838   PetscErrorCode     ierr;
7839 
7840   PetscFunctionBegin;
7841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7842   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7843   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7844   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7845   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7846   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7847 
7848   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7849 
7850   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7851   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7852   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7853   if (ierr) {
7854     PetscMPIInt rank;
7855 
7856     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7857     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7858     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7859     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7860     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7861     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7862   }
7863   if (mesh->printFEM > 1) {
7864     PetscInt i;
7865     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7866     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7867     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7868   }
7869 
7870   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7871   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7872   PetscFunctionReturn(PETSC_SUCCESS);
7873 }
7874 
7875 /*@C
7876   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7877 
7878   Not collective
7879 
7880   Input Parameters:
7881 + dmRow - The `DM` for the row fields
7882 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7883 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7884 . dmCol - The `DM` for the column fields
7885 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7886 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7887 . A - The matrix
7888 . point - The point in the `DM`
7889 . values - The array of values
7890 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7891 
7892   Level: intermediate
7893 
7894 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7895 @*/
7896 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7897 {
7898   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7899   PetscInt          *indicesRow, *indicesCol;
7900   PetscInt           numIndicesRow, numIndicesCol;
7901   const PetscScalar *valuesOrig = values;
7902   PetscErrorCode     ierr;
7903 
7904   PetscFunctionBegin;
7905   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7906   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7907   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7908   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7909   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7910   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7911   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7912   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7913   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7914   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7915   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7916 
7917   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7918   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7919 
7920   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7921   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7922   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7923   if (ierr) {
7924     PetscMPIInt rank;
7925 
7926     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7927     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7928     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7929     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7930     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7931     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7932   }
7933 
7934   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7935   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7936   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7937   PetscFunctionReturn(PETSC_SUCCESS);
7938 }
7939 
7940 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7941 {
7942   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7943   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7944   PetscInt       *cpoints = NULL;
7945   PetscInt       *findices, *cindices;
7946   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7947   PetscInt        foffsets[32], coffsets[32];
7948   DMPolytopeType  ct;
7949   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7950   PetscErrorCode  ierr;
7951 
7952   PetscFunctionBegin;
7953   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7954   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7955   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7956   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7957   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7958   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7959   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7960   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7961   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7962   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7963   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7964   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7965   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7966   PetscCall(PetscArrayzero(foffsets, 32));
7967   PetscCall(PetscArrayzero(coffsets, 32));
7968   /* Column indices */
7969   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7970   maxFPoints = numCPoints;
7971   /* Compress out points not in the section */
7972   /*   TODO: Squeeze out points with 0 dof as well */
7973   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7974   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7975     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7976       cpoints[q * 2]     = cpoints[p];
7977       cpoints[q * 2 + 1] = cpoints[p + 1];
7978       ++q;
7979     }
7980   }
7981   numCPoints = q;
7982   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7983     PetscInt fdof;
7984 
7985     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7986     if (!dof) continue;
7987     for (f = 0; f < numFields; ++f) {
7988       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7989       coffsets[f + 1] += fdof;
7990     }
7991     numCIndices += dof;
7992   }
7993   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7994   /* Row indices */
7995   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7996   {
7997     DMPlexTransform tr;
7998     DMPolytopeType *rct;
7999     PetscInt       *rsize, *rcone, *rornt, Nt;
8000 
8001     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8002     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8003     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8004     numSubcells = rsize[Nt - 1];
8005     PetscCall(DMPlexTransformDestroy(&tr));
8006   }
8007   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8008   for (r = 0, q = 0; r < numSubcells; ++r) {
8009     /* TODO Map from coarse to fine cells */
8010     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8011     /* Compress out points not in the section */
8012     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8013     for (p = 0; p < numFPoints * 2; p += 2) {
8014       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8015         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8016         if (!dof) continue;
8017         for (s = 0; s < q; ++s)
8018           if (fpoints[p] == ftotpoints[s * 2]) break;
8019         if (s < q) continue;
8020         ftotpoints[q * 2]     = fpoints[p];
8021         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8022         ++q;
8023       }
8024     }
8025     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8026   }
8027   numFPoints = q;
8028   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8029     PetscInt fdof;
8030 
8031     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8032     if (!dof) continue;
8033     for (f = 0; f < numFields; ++f) {
8034       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8035       foffsets[f + 1] += fdof;
8036     }
8037     numFIndices += dof;
8038   }
8039   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8040 
8041   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8042   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8043   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8044   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8045   if (numFields) {
8046     const PetscInt **permsF[32] = {NULL};
8047     const PetscInt **permsC[32] = {NULL};
8048 
8049     for (f = 0; f < numFields; f++) {
8050       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8051       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8052     }
8053     for (p = 0; p < numFPoints; p++) {
8054       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8055       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8056     }
8057     for (p = 0; p < numCPoints; p++) {
8058       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8059       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8060     }
8061     for (f = 0; f < numFields; f++) {
8062       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8063       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8064     }
8065   } else {
8066     const PetscInt **permsF = NULL;
8067     const PetscInt **permsC = NULL;
8068 
8069     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8070     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8071     for (p = 0, off = 0; p < numFPoints; p++) {
8072       const PetscInt *perm = permsF ? permsF[p] : NULL;
8073 
8074       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8075       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8076     }
8077     for (p = 0, off = 0; p < numCPoints; p++) {
8078       const PetscInt *perm = permsC ? permsC[p] : NULL;
8079 
8080       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8081       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8082     }
8083     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8084     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8085   }
8086   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8087   /* TODO: flips */
8088   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8089   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8090   if (ierr) {
8091     PetscMPIInt rank;
8092 
8093     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8094     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8095     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8096     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8097     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8098   }
8099   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8100   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8101   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8102   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8103   PetscFunctionReturn(PETSC_SUCCESS);
8104 }
8105 
8106 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8107 {
8108   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8109   PetscInt       *cpoints = NULL;
8110   PetscInt        foffsets[32], coffsets[32];
8111   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8112   DMPolytopeType  ct;
8113   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8114 
8115   PetscFunctionBegin;
8116   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8117   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8118   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8119   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8120   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8121   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8122   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8123   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8124   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8125   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8126   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8127   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8128   PetscCall(PetscArrayzero(foffsets, 32));
8129   PetscCall(PetscArrayzero(coffsets, 32));
8130   /* Column indices */
8131   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8132   maxFPoints = numCPoints;
8133   /* Compress out points not in the section */
8134   /*   TODO: Squeeze out points with 0 dof as well */
8135   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8136   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8137     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8138       cpoints[q * 2]     = cpoints[p];
8139       cpoints[q * 2 + 1] = cpoints[p + 1];
8140       ++q;
8141     }
8142   }
8143   numCPoints = q;
8144   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8145     PetscInt fdof;
8146 
8147     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8148     if (!dof) continue;
8149     for (f = 0; f < numFields; ++f) {
8150       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8151       coffsets[f + 1] += fdof;
8152     }
8153     numCIndices += dof;
8154   }
8155   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8156   /* Row indices */
8157   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8158   {
8159     DMPlexTransform tr;
8160     DMPolytopeType *rct;
8161     PetscInt       *rsize, *rcone, *rornt, Nt;
8162 
8163     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8164     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8165     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8166     numSubcells = rsize[Nt - 1];
8167     PetscCall(DMPlexTransformDestroy(&tr));
8168   }
8169   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8170   for (r = 0, q = 0; r < numSubcells; ++r) {
8171     /* TODO Map from coarse to fine cells */
8172     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8173     /* Compress out points not in the section */
8174     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8175     for (p = 0; p < numFPoints * 2; p += 2) {
8176       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8177         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8178         if (!dof) continue;
8179         for (s = 0; s < q; ++s)
8180           if (fpoints[p] == ftotpoints[s * 2]) break;
8181         if (s < q) continue;
8182         ftotpoints[q * 2]     = fpoints[p];
8183         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8184         ++q;
8185       }
8186     }
8187     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8188   }
8189   numFPoints = q;
8190   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8191     PetscInt fdof;
8192 
8193     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8194     if (!dof) continue;
8195     for (f = 0; f < numFields; ++f) {
8196       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8197       foffsets[f + 1] += fdof;
8198     }
8199     numFIndices += dof;
8200   }
8201   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8202 
8203   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8204   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8205   if (numFields) {
8206     const PetscInt **permsF[32] = {NULL};
8207     const PetscInt **permsC[32] = {NULL};
8208 
8209     for (f = 0; f < numFields; f++) {
8210       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8211       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8212     }
8213     for (p = 0; p < numFPoints; p++) {
8214       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8215       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8216     }
8217     for (p = 0; p < numCPoints; p++) {
8218       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8219       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8220     }
8221     for (f = 0; f < numFields; f++) {
8222       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8223       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8224     }
8225   } else {
8226     const PetscInt **permsF = NULL;
8227     const PetscInt **permsC = NULL;
8228 
8229     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8230     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8231     for (p = 0, off = 0; p < numFPoints; p++) {
8232       const PetscInt *perm = permsF ? permsF[p] : NULL;
8233 
8234       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8235       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8236     }
8237     for (p = 0, off = 0; p < numCPoints; p++) {
8238       const PetscInt *perm = permsC ? permsC[p] : NULL;
8239 
8240       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8241       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8242     }
8243     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8244     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8245   }
8246   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8247   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8248   PetscFunctionReturn(PETSC_SUCCESS);
8249 }
8250 
8251 /*@C
8252   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8253 
8254   Input Parameter:
8255 . dm   - The `DMPLEX` object
8256 
8257   Output Parameter:
8258 . cellHeight - The height of a cell
8259 
8260   Level: developer
8261 
8262 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`,  `DMPlexSetVTKCellHeight()`
8263 @*/
8264 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8265 {
8266   DM_Plex *mesh = (DM_Plex *)dm->data;
8267 
8268   PetscFunctionBegin;
8269   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8270   PetscValidIntPointer(cellHeight, 2);
8271   *cellHeight = mesh->vtkCellHeight;
8272   PetscFunctionReturn(PETSC_SUCCESS);
8273 }
8274 
8275 /*@C
8276   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8277 
8278   Input Parameters:
8279 + dm   - The `DMPLEX` object
8280 - cellHeight - The height of a cell
8281 
8282   Level: developer
8283 
8284 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8285 @*/
8286 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8287 {
8288   DM_Plex *mesh = (DM_Plex *)dm->data;
8289 
8290   PetscFunctionBegin;
8291   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8292   mesh->vtkCellHeight = cellHeight;
8293   PetscFunctionReturn(PETSC_SUCCESS);
8294 }
8295 
8296 /*@
8297   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8298 
8299   Input Parameter:
8300 . dm - The `DMPLEX` object
8301 
8302   Output Parameters:
8303 + gcStart - The first ghost cell, or NULL
8304 - gcEnd   - The upper bound on ghost cells, or NULL
8305 
8306   Level: advanced
8307 
8308 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8309 @*/
8310 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8311 {
8312   DMLabel ctLabel;
8313 
8314   PetscFunctionBegin;
8315   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8316   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8317   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8318   // Reset label for fast lookup
8319   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8320   PetscFunctionReturn(PETSC_SUCCESS);
8321 }
8322 
8323 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8324 {
8325   PetscSection section, globalSection;
8326   PetscInt    *numbers, p;
8327 
8328   PetscFunctionBegin;
8329   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8330   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8331   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8332   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8333   PetscCall(PetscSectionSetUp(section));
8334   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8335   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8336   for (p = pStart; p < pEnd; ++p) {
8337     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8338     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8339     else numbers[p - pStart] += shift;
8340   }
8341   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8342   if (globalSize) {
8343     PetscLayout layout;
8344     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8345     PetscCall(PetscLayoutGetSize(layout, globalSize));
8346     PetscCall(PetscLayoutDestroy(&layout));
8347   }
8348   PetscCall(PetscSectionDestroy(&section));
8349   PetscCall(PetscSectionDestroy(&globalSection));
8350   PetscFunctionReturn(PETSC_SUCCESS);
8351 }
8352 
8353 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8354 {
8355   PetscInt cellHeight, cStart, cEnd;
8356 
8357   PetscFunctionBegin;
8358   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8359   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8360   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8361   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8362   PetscFunctionReturn(PETSC_SUCCESS);
8363 }
8364 
8365 /*@
8366   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8367 
8368   Input Parameter:
8369 . dm   - The `DMPLEX` object
8370 
8371   Output Parameter:
8372 . globalCellNumbers - Global cell numbers for all cells on this process
8373 
8374   Level: developer
8375 
8376 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8377 @*/
8378 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8379 {
8380   DM_Plex *mesh = (DM_Plex *)dm->data;
8381 
8382   PetscFunctionBegin;
8383   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8384   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8385   *globalCellNumbers = mesh->globalCellNumbers;
8386   PetscFunctionReturn(PETSC_SUCCESS);
8387 }
8388 
8389 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8390 {
8391   PetscInt vStart, vEnd;
8392 
8393   PetscFunctionBegin;
8394   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8395   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8396   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8397   PetscFunctionReturn(PETSC_SUCCESS);
8398 }
8399 
8400 /*@
8401   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8402 
8403   Input Parameter:
8404 . dm   - The `DMPLEX` object
8405 
8406   Output Parameter:
8407 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8408 
8409   Level: developer
8410 
8411 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8412 @*/
8413 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8414 {
8415   DM_Plex *mesh = (DM_Plex *)dm->data;
8416 
8417   PetscFunctionBegin;
8418   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8419   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8420   *globalVertexNumbers = mesh->globalVertexNumbers;
8421   PetscFunctionReturn(PETSC_SUCCESS);
8422 }
8423 
8424 /*@
8425   DMPlexCreatePointNumbering - Create a global numbering for all points.
8426 
8427   Collective on dm
8428 
8429   Input Parameter:
8430 . dm   - The `DMPLEX` object
8431 
8432   Output Parameter:
8433 . globalPointNumbers - Global numbers for all points on this process
8434 
8435   Level: developer
8436 
8437   Notes:
8438   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8439   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8440   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8441   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8442 
8443   The partitioned mesh is
8444 ```
8445  (2)--0--(3)--1--(4)    (1)--0--(2)
8446 ```
8447   and its global numbering is
8448 ```
8449   (3)--0--(4)--1--(5)--2--(6)
8450 ```
8451   Then the global numbering is provided as
8452 ```
8453 [0] Number of indices in set 5
8454 [0] 0 0
8455 [0] 1 1
8456 [0] 2 3
8457 [0] 3 4
8458 [0] 4 -6
8459 [1] Number of indices in set 3
8460 [1] 0 2
8461 [1] 1 5
8462 [1] 2 6
8463 ```
8464 
8465 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8466 @*/
8467 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8468 {
8469   IS        nums[4];
8470   PetscInt  depths[4], gdepths[4], starts[4];
8471   PetscInt  depth, d, shift = 0;
8472   PetscBool empty = PETSC_FALSE;
8473 
8474   PetscFunctionBegin;
8475   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8476   PetscCall(DMPlexGetDepth(dm, &depth));
8477   // For unstratified meshes use dim instead of depth
8478   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8479   // If any stratum is empty, we must mark all empty
8480   for (d = 0; d <= depth; ++d) {
8481     PetscInt end;
8482 
8483     depths[d] = depth - d;
8484     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8485     if (!(starts[d] - end)) empty = PETSC_TRUE;
8486   }
8487   if (empty)
8488     for (d = 0; d <= depth; ++d) {
8489       depths[d] = -1;
8490       starts[d] = -1;
8491     }
8492   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8493   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8494   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]);
8495   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8496   for (d = 0; d <= depth; ++d) {
8497     PetscInt pStart, pEnd, gsize;
8498 
8499     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8500     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8501     shift += gsize;
8502   }
8503   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8504   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8505   PetscFunctionReturn(PETSC_SUCCESS);
8506 }
8507 
8508 /*@
8509   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8510 
8511   Input Parameter:
8512 . dm - The `DMPLEX` object
8513 
8514   Output Parameter:
8515 . ranks - The rank field
8516 
8517   Options Database Key:
8518 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8519 
8520   Level: intermediate
8521 
8522 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8523 @*/
8524 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8525 {
8526   DM             rdm;
8527   PetscFE        fe;
8528   PetscScalar   *r;
8529   PetscMPIInt    rank;
8530   DMPolytopeType ct;
8531   PetscInt       dim, cStart, cEnd, c;
8532   PetscBool      simplex;
8533 
8534   PetscFunctionBeginUser;
8535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8536   PetscValidPointer(ranks, 2);
8537   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8538   PetscCall(DMClone(dm, &rdm));
8539   PetscCall(DMGetDimension(rdm, &dim));
8540   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8541   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8542   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8543   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8544   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8545   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8546   PetscCall(PetscFEDestroy(&fe));
8547   PetscCall(DMCreateDS(rdm));
8548   PetscCall(DMCreateGlobalVector(rdm, ranks));
8549   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8550   PetscCall(VecGetArray(*ranks, &r));
8551   for (c = cStart; c < cEnd; ++c) {
8552     PetscScalar *lr;
8553 
8554     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8555     if (lr) *lr = rank;
8556   }
8557   PetscCall(VecRestoreArray(*ranks, &r));
8558   PetscCall(DMDestroy(&rdm));
8559   PetscFunctionReturn(PETSC_SUCCESS);
8560 }
8561 
8562 /*@
8563   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8564 
8565   Input Parameters:
8566 + dm    - The DMPlex
8567 - label - The DMLabel
8568 
8569   Output Parameter:
8570 . val - The label value field
8571 
8572   Options Database Keys:
8573 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8574 
8575   Level: intermediate
8576 
8577 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8578 @*/
8579 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8580 {
8581   DM           rdm;
8582   PetscFE      fe;
8583   PetscScalar *v;
8584   PetscInt     dim, cStart, cEnd, c;
8585 
8586   PetscFunctionBeginUser;
8587   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8588   PetscValidPointer(label, 2);
8589   PetscValidPointer(val, 3);
8590   PetscCall(DMClone(dm, &rdm));
8591   PetscCall(DMGetDimension(rdm, &dim));
8592   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8593   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8594   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8595   PetscCall(PetscFEDestroy(&fe));
8596   PetscCall(DMCreateDS(rdm));
8597   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8598   PetscCall(DMCreateGlobalVector(rdm, val));
8599   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8600   PetscCall(VecGetArray(*val, &v));
8601   for (c = cStart; c < cEnd; ++c) {
8602     PetscScalar *lv;
8603     PetscInt     cval;
8604 
8605     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8606     PetscCall(DMLabelGetValue(label, c, &cval));
8607     *lv = cval;
8608   }
8609   PetscCall(VecRestoreArray(*val, &v));
8610   PetscCall(DMDestroy(&rdm));
8611   PetscFunctionReturn(PETSC_SUCCESS);
8612 }
8613 
8614 /*@
8615   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8616 
8617   Input Parameter:
8618 . dm - The `DMPLEX` object
8619 
8620   Level: developer
8621 
8622   Notes:
8623   This is a useful diagnostic when creating meshes programmatically.
8624 
8625   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8626 
8627 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8628 @*/
8629 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8630 {
8631   PetscSection    coneSection, supportSection;
8632   const PetscInt *cone, *support;
8633   PetscInt        coneSize, c, supportSize, s;
8634   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8635   PetscBool       storagecheck = PETSC_TRUE;
8636 
8637   PetscFunctionBegin;
8638   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8639   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8640   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8641   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8642   /* Check that point p is found in the support of its cone points, and vice versa */
8643   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8644   for (p = pStart; p < pEnd; ++p) {
8645     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8646     PetscCall(DMPlexGetCone(dm, p, &cone));
8647     for (c = 0; c < coneSize; ++c) {
8648       PetscBool dup = PETSC_FALSE;
8649       PetscInt  d;
8650       for (d = c - 1; d >= 0; --d) {
8651         if (cone[c] == cone[d]) {
8652           dup = PETSC_TRUE;
8653           break;
8654         }
8655       }
8656       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8657       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8658       for (s = 0; s < supportSize; ++s) {
8659         if (support[s] == p) break;
8660       }
8661       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8662         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8663         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8664         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8665         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8666         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8667         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8668         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]);
8669         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8670       }
8671     }
8672     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8673     if (p != pp) {
8674       storagecheck = PETSC_FALSE;
8675       continue;
8676     }
8677     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8678     PetscCall(DMPlexGetSupport(dm, p, &support));
8679     for (s = 0; s < supportSize; ++s) {
8680       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8681       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8682       for (c = 0; c < coneSize; ++c) {
8683         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8684         if (cone[c] != pp) {
8685           c = 0;
8686           break;
8687         }
8688         if (cone[c] == p) break;
8689       }
8690       if (c >= coneSize) {
8691         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8692         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8693         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8694         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8695         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8696         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8697         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8698       }
8699     }
8700   }
8701   if (storagecheck) {
8702     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8703     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8704     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8705   }
8706   PetscFunctionReturn(PETSC_SUCCESS);
8707 }
8708 
8709 /*
8710   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.
8711 */
8712 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8713 {
8714   DMPolytopeType  cct;
8715   PetscInt        ptpoints[4];
8716   const PetscInt *cone, *ccone, *ptcone;
8717   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8718 
8719   PetscFunctionBegin;
8720   *unsplit = 0;
8721   switch (ct) {
8722   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8723     ptpoints[npt++] = c;
8724     break;
8725   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8726     PetscCall(DMPlexGetCone(dm, c, &cone));
8727     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8728     for (cp = 0; cp < coneSize; ++cp) {
8729       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8730       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8731     }
8732     break;
8733   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8734   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8735     PetscCall(DMPlexGetCone(dm, c, &cone));
8736     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8737     for (cp = 0; cp < coneSize; ++cp) {
8738       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8739       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8740       for (ccp = 0; ccp < cconeSize; ++ccp) {
8741         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8742         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8743           PetscInt p;
8744           for (p = 0; p < npt; ++p)
8745             if (ptpoints[p] == ccone[ccp]) break;
8746           if (p == npt) ptpoints[npt++] = ccone[ccp];
8747         }
8748       }
8749     }
8750     break;
8751   default:
8752     break;
8753   }
8754   for (pt = 0; pt < npt; ++pt) {
8755     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8756     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8757   }
8758   PetscFunctionReturn(PETSC_SUCCESS);
8759 }
8760 
8761 /*@
8762   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8763 
8764   Input Parameters:
8765 + dm - The `DMPLEX` object
8766 - cellHeight - Normally 0
8767 
8768   Level: developer
8769 
8770   Notes:
8771   This is a useful diagnostic when creating meshes programmatically.
8772   Currently applicable only to homogeneous simplex or tensor meshes.
8773 
8774   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8775 
8776 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8777 @*/
8778 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8779 {
8780   DMPlexInterpolatedFlag interp;
8781   DMPolytopeType         ct;
8782   PetscInt               vStart, vEnd, cStart, cEnd, c;
8783 
8784   PetscFunctionBegin;
8785   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8786   PetscCall(DMPlexIsInterpolated(dm, &interp));
8787   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8788   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8789   for (c = cStart; c < cEnd; ++c) {
8790     PetscInt *closure = NULL;
8791     PetscInt  coneSize, closureSize, cl, Nv = 0;
8792 
8793     PetscCall(DMPlexGetCellType(dm, c, &ct));
8794     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8795     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8796     if (interp == DMPLEX_INTERPOLATED_FULL) {
8797       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8798       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));
8799     }
8800     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8801     for (cl = 0; cl < closureSize * 2; cl += 2) {
8802       const PetscInt p = closure[cl];
8803       if ((p >= vStart) && (p < vEnd)) ++Nv;
8804     }
8805     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8806     /* Special Case: Tensor faces with identified vertices */
8807     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8808       PetscInt unsplit;
8809 
8810       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8811       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8812     }
8813     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));
8814   }
8815   PetscFunctionReturn(PETSC_SUCCESS);
8816 }
8817 
8818 /*@
8819   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8820 
8821   Collective on dm
8822 
8823   Input Parameters:
8824 + dm - The `DMPLEX` object
8825 - cellHeight - Normally 0
8826 
8827   Level: developer
8828 
8829   Notes:
8830   This is a useful diagnostic when creating meshes programmatically.
8831   This routine is only relevant for meshes that are fully interpolated across all ranks.
8832   It will error out if a partially interpolated mesh is given on some rank.
8833   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8834 
8835   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8836 
8837 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8838 @*/
8839 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8840 {
8841   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8842   DMPlexInterpolatedFlag interpEnum;
8843 
8844   PetscFunctionBegin;
8845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8846   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8847   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8848   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8849     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8850     PetscFunctionReturn(PETSC_SUCCESS);
8851   }
8852 
8853   PetscCall(DMGetDimension(dm, &dim));
8854   PetscCall(DMPlexGetDepth(dm, &depth));
8855   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8856   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8857     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8858     for (c = cStart; c < cEnd; ++c) {
8859       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8860       const DMPolytopeType *faceTypes;
8861       DMPolytopeType        ct;
8862       PetscInt              numFaces, coneSize, f;
8863       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8864 
8865       PetscCall(DMPlexGetCellType(dm, c, &ct));
8866       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8867       if (unsplit) continue;
8868       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8869       PetscCall(DMPlexGetCone(dm, c, &cone));
8870       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8871       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8872       for (cl = 0; cl < closureSize * 2; cl += 2) {
8873         const PetscInt p = closure[cl];
8874         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8875       }
8876       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8877       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);
8878       for (f = 0; f < numFaces; ++f) {
8879         DMPolytopeType fct;
8880         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8881 
8882         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8883         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8884         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8885           const PetscInt p = fclosure[cl];
8886           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8887         }
8888         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]);
8889         for (v = 0; v < fnumCorners; ++v) {
8890           if (fclosure[v] != faces[fOff + v]) {
8891             PetscInt v1;
8892 
8893             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8894             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8895             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8896             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8897             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8898             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]);
8899           }
8900         }
8901         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8902         fOff += faceSizes[f];
8903       }
8904       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8905       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8906     }
8907   }
8908   PetscFunctionReturn(PETSC_SUCCESS);
8909 }
8910 
8911 /*@
8912   DMPlexCheckGeometry - Check the geometry of mesh cells
8913 
8914   Input Parameter:
8915 . dm - The `DMPLEX` object
8916 
8917   Level: developer
8918 
8919   Notes:
8920   This is a useful diagnostic when creating meshes programmatically.
8921 
8922   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8923 
8924 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8925 @*/
8926 PetscErrorCode DMPlexCheckGeometry(DM dm)
8927 {
8928   Vec       coordinates;
8929   PetscReal detJ, J[9], refVol = 1.0;
8930   PetscReal vol;
8931   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8932 
8933   PetscFunctionBegin;
8934   PetscCall(DMGetDimension(dm, &dim));
8935   PetscCall(DMGetCoordinateDim(dm, &dE));
8936   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
8937   PetscCall(DMPlexGetDepth(dm, &depth));
8938   for (d = 0; d < dim; ++d) refVol *= 2.0;
8939   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8940   /* Make sure local coordinates are created, because that step is collective */
8941   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8942   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
8943   for (c = cStart; c < cEnd; ++c) {
8944     DMPolytopeType ct;
8945     PetscInt       unsplit;
8946     PetscBool      ignoreZeroVol = PETSC_FALSE;
8947 
8948     PetscCall(DMPlexGetCellType(dm, c, &ct));
8949     switch (ct) {
8950     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8951     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8952     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8953       ignoreZeroVol = PETSC_TRUE;
8954       break;
8955     default:
8956       break;
8957     }
8958     switch (ct) {
8959     case DM_POLYTOPE_TRI_PRISM:
8960     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8961     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8962     case DM_POLYTOPE_PYRAMID:
8963       continue;
8964     default:
8965       break;
8966     }
8967     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8968     if (unsplit) continue;
8969     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8970     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);
8971     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8972     /* This should work with periodicity since DG coordinates should be used */
8973     if (depth > 1) {
8974       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8975       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);
8976       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8977     }
8978   }
8979   PetscFunctionReturn(PETSC_SUCCESS);
8980 }
8981 
8982 /*@
8983   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8984 
8985   Collective on dm
8986 
8987   Input Parameters:
8988 + dm - The `DMPLEX` object
8989 . pointSF - The `PetscSF`, or NULL for `PointSF` attached to `DM`
8990 - allowExtraRoots - Flag to allow extra points not present in the `DM`
8991 
8992   Level: developer
8993 
8994   Notes:
8995   This is mainly intended for debugging/testing purposes.
8996 
8997   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8998 
8999   Extra roots can come from priodic cuts, where additional points appear on the boundary
9000 
9001 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9002 @*/
9003 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9004 {
9005   PetscInt           l, nleaves, nroots, overlap;
9006   const PetscInt    *locals;
9007   const PetscSFNode *remotes;
9008   PetscBool          distributed;
9009   MPI_Comm           comm;
9010   PetscMPIInt        rank;
9011 
9012   PetscFunctionBegin;
9013   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9014   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9015   else pointSF = dm->sf;
9016   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9017   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9018   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9019   {
9020     PetscMPIInt mpiFlag;
9021 
9022     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9023     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9024   }
9025   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9026   PetscCall(DMPlexIsDistributed(dm, &distributed));
9027   if (!distributed) {
9028     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);
9029     PetscFunctionReturn(PETSC_SUCCESS);
9030   }
9031   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);
9032   PetscCall(DMPlexGetOverlap(dm, &overlap));
9033 
9034   /* Check SF graph is compatible with DMPlex chart */
9035   {
9036     PetscInt pStart, pEnd, maxLeaf;
9037 
9038     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9039     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9040     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9041     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9042   }
9043 
9044   /* Check Point SF has no local points referenced */
9045   for (l = 0; l < nleaves; l++) {
9046     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);
9047   }
9048 
9049   /* Check there are no cells in interface */
9050   if (!overlap) {
9051     PetscInt cellHeight, cStart, cEnd;
9052 
9053     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9054     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9055     for (l = 0; l < nleaves; ++l) {
9056       const PetscInt point = locals ? locals[l] : l;
9057 
9058       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9059     }
9060   }
9061 
9062   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9063   {
9064     const PetscInt *rootdegree;
9065 
9066     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9067     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9068     for (l = 0; l < nleaves; ++l) {
9069       const PetscInt  point = locals ? locals[l] : l;
9070       const PetscInt *cone;
9071       PetscInt        coneSize, c, idx;
9072 
9073       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9074       PetscCall(DMPlexGetCone(dm, point, &cone));
9075       for (c = 0; c < coneSize; ++c) {
9076         if (!rootdegree[cone[c]]) {
9077           if (locals) {
9078             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9079           } else {
9080             idx = (cone[c] < nleaves) ? cone[c] : -1;
9081           }
9082           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9083         }
9084       }
9085     }
9086   }
9087   PetscFunctionReturn(PETSC_SUCCESS);
9088 }
9089 
9090 /*@
9091   DMPlexCheck - Perform various checks of Plex sanity
9092 
9093   Input Parameter:
9094 . dm - The `DMPLEX` object
9095 
9096   Level: developer
9097 
9098   Notes:
9099   This is a useful diagnostic when creating meshes programmatically.
9100 
9101   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
9102 
9103   Currently does not include DMPlexCheckCellShape().
9104 
9105 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, DMCreate(), DMSetFromOptions()
9106 @*/
9107 PetscErrorCode DMPlexCheck(DM dm)
9108 {
9109   PetscInt cellHeight;
9110 
9111   PetscFunctionBegin;
9112   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9113   PetscCall(DMPlexCheckSymmetry(dm));
9114   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9115   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9116   PetscCall(DMPlexCheckGeometry(dm));
9117   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9118   PetscCall(DMPlexCheckInterfaceCones(dm));
9119   PetscFunctionReturn(PETSC_SUCCESS);
9120 }
9121 
9122 typedef struct cell_stats {
9123   PetscReal min, max, sum, squaresum;
9124   PetscInt  count;
9125 } cell_stats_t;
9126 
9127 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9128 {
9129   PetscInt i, N = *len;
9130 
9131   for (i = 0; i < N; i++) {
9132     cell_stats_t *A = (cell_stats_t *)a;
9133     cell_stats_t *B = (cell_stats_t *)b;
9134 
9135     B->min = PetscMin(A->min, B->min);
9136     B->max = PetscMax(A->max, B->max);
9137     B->sum += A->sum;
9138     B->squaresum += A->squaresum;
9139     B->count += A->count;
9140   }
9141 }
9142 
9143 /*@
9144   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9145 
9146   Collective on dm
9147 
9148   Input Parameters:
9149 + dm        - The `DMPLEX` object
9150 . output    - If true, statistics will be displayed on stdout
9151 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9152 
9153   Level: developer
9154 
9155   Notes:
9156   This is mainly intended for debugging/testing purposes.
9157 
9158   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9159 
9160 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9161 @*/
9162 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9163 {
9164   DM           dmCoarse;
9165   cell_stats_t stats, globalStats;
9166   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9167   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9168   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9169   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9170   PetscMPIInt  rank, size;
9171 
9172   PetscFunctionBegin;
9173   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9174   stats.min = PETSC_MAX_REAL;
9175   stats.max = PETSC_MIN_REAL;
9176   stats.sum = stats.squaresum = 0.;
9177   stats.count                 = 0;
9178 
9179   PetscCallMPI(MPI_Comm_size(comm, &size));
9180   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9181   PetscCall(DMGetCoordinateDim(dm, &cdim));
9182   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9183   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9184   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9185   for (c = cStart; c < cEnd; c++) {
9186     PetscInt  i;
9187     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9188 
9189     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9190     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9191     for (i = 0; i < PetscSqr(cdim); ++i) {
9192       frobJ += J[i] * J[i];
9193       frobInvJ += invJ[i] * invJ[i];
9194     }
9195     cond2 = frobJ * frobInvJ;
9196     cond  = PetscSqrtReal(cond2);
9197 
9198     stats.min = PetscMin(stats.min, cond);
9199     stats.max = PetscMax(stats.max, cond);
9200     stats.sum += cond;
9201     stats.squaresum += cond2;
9202     stats.count++;
9203     if (output && cond > limit) {
9204       PetscSection coordSection;
9205       Vec          coordsLocal;
9206       PetscScalar *coords = NULL;
9207       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9208 
9209       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9210       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9211       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9212       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9213       for (i = 0; i < Nv / cdim; ++i) {
9214         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9215         for (d = 0; d < cdim; ++d) {
9216           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9217           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9218         }
9219         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9220       }
9221       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9222       for (cl = 0; cl < clSize * 2; cl += 2) {
9223         const PetscInt edge = closure[cl];
9224 
9225         if ((edge >= eStart) && (edge < eEnd)) {
9226           PetscReal len;
9227 
9228           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9229           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9230         }
9231       }
9232       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9233       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9234     }
9235   }
9236   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9237 
9238   if (size > 1) {
9239     PetscMPIInt  blockLengths[2] = {4, 1};
9240     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9241     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9242     MPI_Op       statReduce;
9243 
9244     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9245     PetscCallMPI(MPI_Type_commit(&statType));
9246     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9247     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9248     PetscCallMPI(MPI_Op_free(&statReduce));
9249     PetscCallMPI(MPI_Type_free(&statType));
9250   } else {
9251     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9252   }
9253   if (rank == 0) {
9254     count = globalStats.count;
9255     min   = globalStats.min;
9256     max   = globalStats.max;
9257     mean  = globalStats.sum / globalStats.count;
9258     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9259   }
9260 
9261   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));
9262   PetscCall(PetscFree2(J, invJ));
9263 
9264   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9265   if (dmCoarse) {
9266     PetscBool isplex;
9267 
9268     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9269     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9270   }
9271   PetscFunctionReturn(PETSC_SUCCESS);
9272 }
9273 
9274 /*@
9275   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9276   orthogonal quality below given tolerance.
9277 
9278   Collective on dm
9279 
9280   Input Parameters:
9281 + dm   - The `DMPLEX` object
9282 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9283 - atol - [0, 1] Absolute tolerance for tagging cells.
9284 
9285   Output Parameters:
9286 + OrthQual      - Vec containing orthogonal quality per cell
9287 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9288 
9289   Options Database Keys:
9290 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9291 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9292 
9293   Level: intermediate
9294 
9295   Notes:
9296   Orthogonal quality is given by the following formula:
9297 
9298   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9299 
9300   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
9301   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9302   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9303   calculating the cosine of the angle between these vectors.
9304 
9305   Orthogonal quality ranges from 1 (best) to 0 (worst).
9306 
9307   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9308   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9309 
9310   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9311 
9312 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9313 @*/
9314 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9315 {
9316   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9317   PetscInt              *idx;
9318   PetscScalar           *oqVals;
9319   const PetscScalar     *cellGeomArr, *faceGeomArr;
9320   PetscReal             *ci, *fi, *Ai;
9321   MPI_Comm               comm;
9322   Vec                    cellgeom, facegeom;
9323   DM                     dmFace, dmCell;
9324   IS                     glob;
9325   ISLocalToGlobalMapping ltog;
9326   PetscViewer            vwr;
9327 
9328   PetscFunctionBegin;
9329   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9330   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9331   PetscValidPointer(OrthQual, 4);
9332   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9333   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9334   PetscCall(DMGetDimension(dm, &nc));
9335   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9336   {
9337     DMPlexInterpolatedFlag interpFlag;
9338 
9339     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9340     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9341       PetscMPIInt rank;
9342 
9343       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9344       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9345     }
9346   }
9347   if (OrthQualLabel) {
9348     PetscValidPointer(OrthQualLabel, 5);
9349     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9350     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9351   } else {
9352     *OrthQualLabel = NULL;
9353   }
9354   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9355   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9356   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9357   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9358   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9359   PetscCall(VecCreate(comm, OrthQual));
9360   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9361   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9362   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9363   PetscCall(VecSetUp(*OrthQual));
9364   PetscCall(ISDestroy(&glob));
9365   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9366   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9367   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9368   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9369   PetscCall(VecGetDM(cellgeom, &dmCell));
9370   PetscCall(VecGetDM(facegeom, &dmFace));
9371   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9372   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9373     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9374     PetscInt         cellarr[2], *adj = NULL;
9375     PetscScalar     *cArr, *fArr;
9376     PetscReal        minvalc = 1.0, minvalf = 1.0;
9377     PetscFVCellGeom *cg;
9378 
9379     idx[cellIter] = cell - cStart;
9380     cellarr[0]    = cell;
9381     /* Make indexing into cellGeom easier */
9382     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9383     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9384     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9385     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9386     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9387       PetscInt         i;
9388       const PetscInt   neigh  = adj[cellneigh];
9389       PetscReal        normci = 0, normfi = 0, normai = 0;
9390       PetscFVCellGeom *cgneigh;
9391       PetscFVFaceGeom *fg;
9392 
9393       /* Don't count ourselves in the neighbor list */
9394       if (neigh == cell) continue;
9395       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9396       cellarr[1] = neigh;
9397       {
9398         PetscInt        numcovpts;
9399         const PetscInt *covpts;
9400 
9401         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9402         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9403         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9404       }
9405 
9406       /* Compute c_i, f_i and their norms */
9407       for (i = 0; i < nc; i++) {
9408         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9409         fi[i] = fg->centroid[i] - cg->centroid[i];
9410         Ai[i] = fg->normal[i];
9411         normci += PetscPowReal(ci[i], 2);
9412         normfi += PetscPowReal(fi[i], 2);
9413         normai += PetscPowReal(Ai[i], 2);
9414       }
9415       normci = PetscSqrtReal(normci);
9416       normfi = PetscSqrtReal(normfi);
9417       normai = PetscSqrtReal(normai);
9418 
9419       /* Normalize and compute for each face-cell-normal pair */
9420       for (i = 0; i < nc; i++) {
9421         ci[i] = ci[i] / normci;
9422         fi[i] = fi[i] / normfi;
9423         Ai[i] = Ai[i] / normai;
9424         /* PetscAbs because I don't know if normals are guaranteed to point out */
9425         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9426         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9427       }
9428       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9429       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9430     }
9431     PetscCall(PetscFree(adj));
9432     PetscCall(PetscFree2(cArr, fArr));
9433     /* Defer to cell if they're equal */
9434     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9435     if (OrthQualLabel) {
9436       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9437     }
9438   }
9439   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9440   PetscCall(VecAssemblyBegin(*OrthQual));
9441   PetscCall(VecAssemblyEnd(*OrthQual));
9442   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9443   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9444   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9445   if (OrthQualLabel) {
9446     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9447   }
9448   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9449   PetscCall(PetscViewerDestroy(&vwr));
9450   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9451   PetscFunctionReturn(PETSC_SUCCESS);
9452 }
9453 
9454 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9455  * interpolator construction */
9456 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9457 {
9458   PetscSection section, newSection, gsection;
9459   PetscSF      sf;
9460   PetscBool    hasConstraints, ghasConstraints;
9461 
9462   PetscFunctionBegin;
9463   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9464   PetscValidPointer(odm, 2);
9465   PetscCall(DMGetLocalSection(dm, &section));
9466   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9467   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9468   if (!ghasConstraints) {
9469     PetscCall(PetscObjectReference((PetscObject)dm));
9470     *odm = dm;
9471     PetscFunctionReturn(PETSC_SUCCESS);
9472   }
9473   PetscCall(DMClone(dm, odm));
9474   PetscCall(DMCopyFields(dm, *odm));
9475   PetscCall(DMGetLocalSection(*odm, &newSection));
9476   PetscCall(DMGetPointSF(*odm, &sf));
9477   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9478   PetscCall(DMSetGlobalSection(*odm, gsection));
9479   PetscCall(PetscSectionDestroy(&gsection));
9480   PetscFunctionReturn(PETSC_SUCCESS);
9481 }
9482 
9483 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9484 {
9485   DM        dmco, dmfo;
9486   Mat       interpo;
9487   Vec       rscale;
9488   Vec       cglobalo, clocal;
9489   Vec       fglobal, fglobalo, flocal;
9490   PetscBool regular;
9491 
9492   PetscFunctionBegin;
9493   PetscCall(DMGetFullDM(dmc, &dmco));
9494   PetscCall(DMGetFullDM(dmf, &dmfo));
9495   PetscCall(DMSetCoarseDM(dmfo, dmco));
9496   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9497   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9498   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9499   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9500   PetscCall(DMCreateLocalVector(dmc, &clocal));
9501   PetscCall(VecSet(cglobalo, 0.));
9502   PetscCall(VecSet(clocal, 0.));
9503   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9504   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9505   PetscCall(DMCreateLocalVector(dmf, &flocal));
9506   PetscCall(VecSet(fglobal, 0.));
9507   PetscCall(VecSet(fglobalo, 0.));
9508   PetscCall(VecSet(flocal, 0.));
9509   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9510   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9511   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9512   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9513   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9514   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9515   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9516   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9517   *shift = fglobal;
9518   PetscCall(VecDestroy(&flocal));
9519   PetscCall(VecDestroy(&fglobalo));
9520   PetscCall(VecDestroy(&clocal));
9521   PetscCall(VecDestroy(&cglobalo));
9522   PetscCall(VecDestroy(&rscale));
9523   PetscCall(MatDestroy(&interpo));
9524   PetscCall(DMDestroy(&dmfo));
9525   PetscCall(DMDestroy(&dmco));
9526   PetscFunctionReturn(PETSC_SUCCESS);
9527 }
9528 
9529 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9530 {
9531   PetscObject shifto;
9532   Vec         shift;
9533 
9534   PetscFunctionBegin;
9535   if (!interp) {
9536     Vec rscale;
9537 
9538     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9539     PetscCall(VecDestroy(&rscale));
9540   } else {
9541     PetscCall(PetscObjectReference((PetscObject)interp));
9542   }
9543   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9544   if (!shifto) {
9545     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9546     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9547     shifto = (PetscObject)shift;
9548     PetscCall(VecDestroy(&shift));
9549   }
9550   shift = (Vec)shifto;
9551   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9552   PetscCall(VecAXPY(fineSol, 1.0, shift));
9553   PetscCall(MatDestroy(&interp));
9554   PetscFunctionReturn(PETSC_SUCCESS);
9555 }
9556 
9557 /* Pointwise interpolation
9558      Just code FEM for now
9559      u^f = I u^c
9560      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9561      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9562      I_{ij} = psi^f_i phi^c_j
9563 */
9564 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9565 {
9566   PetscSection gsc, gsf;
9567   PetscInt     m, n;
9568   void        *ctx;
9569   DM           cdm;
9570   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9571 
9572   PetscFunctionBegin;
9573   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9574   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9575   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9576   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9577 
9578   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9579   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9580   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9581   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9582   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9583 
9584   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9585   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9586   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9587   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9588   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9589   if (scaling) {
9590     /* Use naive scaling */
9591     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9592   }
9593   PetscFunctionReturn(PETSC_SUCCESS);
9594 }
9595 
9596 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9597 {
9598   VecScatter ctx;
9599 
9600   PetscFunctionBegin;
9601   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9602   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9603   PetscCall(VecScatterDestroy(&ctx));
9604   PetscFunctionReturn(PETSC_SUCCESS);
9605 }
9606 
9607 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[])
9608 {
9609   const PetscInt Nc = uOff[1] - uOff[0];
9610   PetscInt       c;
9611   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9612 }
9613 
9614 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9615 {
9616   DM           dmc;
9617   PetscDS      ds;
9618   Vec          ones, locmass;
9619   IS           cellIS;
9620   PetscFormKey key;
9621   PetscInt     depth;
9622 
9623   PetscFunctionBegin;
9624   PetscCall(DMClone(dm, &dmc));
9625   PetscCall(DMCopyDisc(dm, dmc));
9626   PetscCall(DMGetDS(dmc, &ds));
9627   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9628   PetscCall(DMCreateGlobalVector(dmc, mass));
9629   PetscCall(DMGetLocalVector(dmc, &ones));
9630   PetscCall(DMGetLocalVector(dmc, &locmass));
9631   PetscCall(DMPlexGetDepth(dmc, &depth));
9632   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9633   PetscCall(VecSet(locmass, 0.0));
9634   PetscCall(VecSet(ones, 1.0));
9635   key.label = NULL;
9636   key.value = 0;
9637   key.field = 0;
9638   key.part  = 0;
9639   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9640   PetscCall(ISDestroy(&cellIS));
9641   PetscCall(VecSet(*mass, 0.0));
9642   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9643   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9644   PetscCall(DMRestoreLocalVector(dmc, &ones));
9645   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9646   PetscCall(DMDestroy(&dmc));
9647   PetscFunctionReturn(PETSC_SUCCESS);
9648 }
9649 
9650 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9651 {
9652   PetscSection gsc, gsf;
9653   PetscInt     m, n;
9654   void        *ctx;
9655   DM           cdm;
9656   PetscBool    regular;
9657 
9658   PetscFunctionBegin;
9659   if (dmFine == dmCoarse) {
9660     DM            dmc;
9661     PetscDS       ds;
9662     PetscWeakForm wf;
9663     Vec           u;
9664     IS            cellIS;
9665     PetscFormKey  key;
9666     PetscInt      depth;
9667 
9668     PetscCall(DMClone(dmFine, &dmc));
9669     PetscCall(DMCopyDisc(dmFine, dmc));
9670     PetscCall(DMGetDS(dmc, &ds));
9671     PetscCall(PetscDSGetWeakForm(ds, &wf));
9672     PetscCall(PetscWeakFormClear(wf));
9673     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9674     PetscCall(DMCreateMatrix(dmc, mass));
9675     PetscCall(DMGetLocalVector(dmc, &u));
9676     PetscCall(DMPlexGetDepth(dmc, &depth));
9677     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9678     PetscCall(MatZeroEntries(*mass));
9679     key.label = NULL;
9680     key.value = 0;
9681     key.field = 0;
9682     key.part  = 0;
9683     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9684     PetscCall(ISDestroy(&cellIS));
9685     PetscCall(DMRestoreLocalVector(dmc, &u));
9686     PetscCall(DMDestroy(&dmc));
9687   } else {
9688     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9689     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9690     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9691     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9692 
9693     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9694     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9695     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9696     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9697 
9698     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9699     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9700     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9701     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9702   }
9703   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9704   PetscFunctionReturn(PETSC_SUCCESS);
9705 }
9706 
9707 /*@
9708   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9709 
9710   Input Parameter:
9711 . dm - The `DMPLEX` object
9712 
9713   Output Parameter:
9714 . regular - The flag
9715 
9716   Level: intermediate
9717 
9718 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9719 @*/
9720 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9721 {
9722   PetscFunctionBegin;
9723   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9724   PetscValidBoolPointer(regular, 2);
9725   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9726   PetscFunctionReturn(PETSC_SUCCESS);
9727 }
9728 
9729 /*@
9730   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9731 
9732   Input Parameters:
9733 + dm - The `DMPLEX` object
9734 - regular - The flag
9735 
9736   Level: intermediate
9737 
9738 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9739 @*/
9740 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9741 {
9742   PetscFunctionBegin;
9743   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9744   ((DM_Plex *)dm->data)->regularRefinement = regular;
9745   PetscFunctionReturn(PETSC_SUCCESS);
9746 }
9747 
9748 /*@
9749   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9750   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9751 
9752   Not Collective
9753 
9754   Input Parameter:
9755 . dm - The `DMPLEX` object
9756 
9757   Output Parameters:
9758 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9759 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9760 
9761   Level: intermediate
9762 
9763 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9764 @*/
9765 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9766 {
9767   DM_Plex *plex = (DM_Plex *)dm->data;
9768 
9769   PetscFunctionBegin;
9770   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9771   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9772   if (anchorSection) *anchorSection = plex->anchorSection;
9773   if (anchorIS) *anchorIS = plex->anchorIS;
9774   PetscFunctionReturn(PETSC_SUCCESS);
9775 }
9776 
9777 /*@
9778   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9779   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9780   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9781 
9782   Collective on dm
9783 
9784   Input Parameters:
9785 + dm - The `DMPLEX` object
9786 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9787                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9788 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9789 
9790   Level: intermediate
9791 
9792   Notes:
9793   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9794   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9795 
9796   The reference counts of anchorSection and anchorIS are incremented.
9797 
9798 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9799 @*/
9800 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9801 {
9802   DM_Plex    *plex = (DM_Plex *)dm->data;
9803   PetscMPIInt result;
9804 
9805   PetscFunctionBegin;
9806   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9807   if (anchorSection) {
9808     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9809     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9810     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9811   }
9812   if (anchorIS) {
9813     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9814     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9815     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9816   }
9817 
9818   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9819   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9820   plex->anchorSection = anchorSection;
9821 
9822   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9823   PetscCall(ISDestroy(&plex->anchorIS));
9824   plex->anchorIS = anchorIS;
9825 
9826   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9827     PetscInt        size, a, pStart, pEnd;
9828     const PetscInt *anchors;
9829 
9830     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9831     PetscCall(ISGetLocalSize(anchorIS, &size));
9832     PetscCall(ISGetIndices(anchorIS, &anchors));
9833     for (a = 0; a < size; a++) {
9834       PetscInt p;
9835 
9836       p = anchors[a];
9837       if (p >= pStart && p < pEnd) {
9838         PetscInt dof;
9839 
9840         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9841         if (dof) {
9842           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9843           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9844         }
9845       }
9846     }
9847     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9848   }
9849   /* reset the generic constraints */
9850   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9851   PetscFunctionReturn(PETSC_SUCCESS);
9852 }
9853 
9854 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9855 {
9856   PetscSection anchorSection;
9857   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9858 
9859   PetscFunctionBegin;
9860   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9861   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9862   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9863   PetscCall(PetscSectionGetNumFields(section, &numFields));
9864   if (numFields) {
9865     PetscInt f;
9866     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9867 
9868     for (f = 0; f < numFields; f++) {
9869       PetscInt numComp;
9870 
9871       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9872       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9873     }
9874   }
9875   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9876   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9877   pStart = PetscMax(pStart, sStart);
9878   pEnd   = PetscMin(pEnd, sEnd);
9879   pEnd   = PetscMax(pStart, pEnd);
9880   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9881   for (p = pStart; p < pEnd; p++) {
9882     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9883     if (dof) {
9884       PetscCall(PetscSectionGetDof(section, p, &dof));
9885       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9886       for (f = 0; f < numFields; f++) {
9887         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9888         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9889       }
9890     }
9891   }
9892   PetscCall(PetscSectionSetUp(*cSec));
9893   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9894   PetscFunctionReturn(PETSC_SUCCESS);
9895 }
9896 
9897 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9898 {
9899   PetscSection    aSec;
9900   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9901   const PetscInt *anchors;
9902   PetscInt        numFields, f;
9903   IS              aIS;
9904   MatType         mtype;
9905   PetscBool       iscuda, iskokkos;
9906 
9907   PetscFunctionBegin;
9908   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9909   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9910   PetscCall(PetscSectionGetStorageSize(section, &n));
9911   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9912   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9913   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9914   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9915   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9916   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9917   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9918   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9919   else mtype = MATSEQAIJ;
9920   PetscCall(MatSetType(*cMat, mtype));
9921   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9922   PetscCall(ISGetIndices(aIS, &anchors));
9923   /* cSec will be a subset of aSec and section */
9924   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9925   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9926   PetscCall(PetscMalloc1(m + 1, &i));
9927   i[0] = 0;
9928   PetscCall(PetscSectionGetNumFields(section, &numFields));
9929   for (p = pStart; p < pEnd; p++) {
9930     PetscInt rDof, rOff, r;
9931 
9932     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9933     if (!rDof) continue;
9934     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9935     if (numFields) {
9936       for (f = 0; f < numFields; f++) {
9937         annz = 0;
9938         for (r = 0; r < rDof; r++) {
9939           a = anchors[rOff + r];
9940           if (a < sStart || a >= sEnd) continue;
9941           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9942           annz += aDof;
9943         }
9944         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9945         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9946         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9947       }
9948     } else {
9949       annz = 0;
9950       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9951       for (q = 0; q < dof; q++) {
9952         a = anchors[rOff + q];
9953         if (a < sStart || a >= sEnd) continue;
9954         PetscCall(PetscSectionGetDof(section, a, &aDof));
9955         annz += aDof;
9956       }
9957       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9958       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9959       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9960     }
9961   }
9962   nnz = i[m];
9963   PetscCall(PetscMalloc1(nnz, &j));
9964   offset = 0;
9965   for (p = pStart; p < pEnd; p++) {
9966     if (numFields) {
9967       for (f = 0; f < numFields; f++) {
9968         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9969         for (q = 0; q < dof; q++) {
9970           PetscInt rDof, rOff, r;
9971           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9972           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9973           for (r = 0; r < rDof; r++) {
9974             PetscInt s;
9975 
9976             a = anchors[rOff + r];
9977             if (a < sStart || a >= sEnd) continue;
9978             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9979             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9980             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9981           }
9982         }
9983       }
9984     } else {
9985       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9986       for (q = 0; q < dof; q++) {
9987         PetscInt rDof, rOff, r;
9988         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9989         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9990         for (r = 0; r < rDof; r++) {
9991           PetscInt s;
9992 
9993           a = anchors[rOff + r];
9994           if (a < sStart || a >= sEnd) continue;
9995           PetscCall(PetscSectionGetDof(section, a, &aDof));
9996           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9997           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9998         }
9999       }
10000     }
10001   }
10002   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10003   PetscCall(PetscFree(i));
10004   PetscCall(PetscFree(j));
10005   PetscCall(ISRestoreIndices(aIS, &anchors));
10006   PetscFunctionReturn(PETSC_SUCCESS);
10007 }
10008 
10009 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10010 {
10011   DM_Plex     *plex = (DM_Plex *)dm->data;
10012   PetscSection anchorSection, section, cSec;
10013   Mat          cMat;
10014 
10015   PetscFunctionBegin;
10016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10017   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10018   if (anchorSection) {
10019     PetscInt Nf;
10020 
10021     PetscCall(DMGetLocalSection(dm, &section));
10022     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10023     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10024     PetscCall(DMGetNumFields(dm, &Nf));
10025     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10026     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10027     PetscCall(PetscSectionDestroy(&cSec));
10028     PetscCall(MatDestroy(&cMat));
10029   }
10030   PetscFunctionReturn(PETSC_SUCCESS);
10031 }
10032 
10033 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10034 {
10035   IS           subis;
10036   PetscSection section, subsection;
10037 
10038   PetscFunctionBegin;
10039   PetscCall(DMGetLocalSection(dm, &section));
10040   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10041   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10042   /* Create subdomain */
10043   PetscCall(DMPlexFilter(dm, label, value, subdm));
10044   /* Create submodel */
10045   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10046   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10047   PetscCall(DMSetLocalSection(*subdm, subsection));
10048   PetscCall(PetscSectionDestroy(&subsection));
10049   PetscCall(DMCopyDisc(dm, *subdm));
10050   /* Create map from submodel to global model */
10051   if (is) {
10052     PetscSection    sectionGlobal, subsectionGlobal;
10053     IS              spIS;
10054     const PetscInt *spmap;
10055     PetscInt       *subIndices;
10056     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10057     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10058 
10059     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10060     PetscCall(ISGetIndices(spIS, &spmap));
10061     PetscCall(PetscSectionGetNumFields(section, &Nf));
10062     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10063     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10064     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10065     for (p = pStart; p < pEnd; ++p) {
10066       PetscInt gdof, pSubSize = 0;
10067 
10068       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10069       if (gdof > 0) {
10070         for (f = 0; f < Nf; ++f) {
10071           PetscInt fdof, fcdof;
10072 
10073           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10074           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10075           pSubSize += fdof - fcdof;
10076         }
10077         subSize += pSubSize;
10078         if (pSubSize) {
10079           if (bs < 0) {
10080             bs = pSubSize;
10081           } else if (bs != pSubSize) {
10082             /* Layout does not admit a pointwise block size */
10083             bs = 1;
10084           }
10085         }
10086       }
10087     }
10088     /* Must have same blocksize on all procs (some might have no points) */
10089     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10090     bsLocal[1] = bs;
10091     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10092     if (bsMinMax[0] != bsMinMax[1]) {
10093       bs = 1;
10094     } else {
10095       bs = bsMinMax[0];
10096     }
10097     PetscCall(PetscMalloc1(subSize, &subIndices));
10098     for (p = pStart; p < pEnd; ++p) {
10099       PetscInt gdof, goff;
10100 
10101       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10102       if (gdof > 0) {
10103         const PetscInt point = spmap[p];
10104 
10105         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10106         for (f = 0; f < Nf; ++f) {
10107           PetscInt fdof, fcdof, fc, f2, poff = 0;
10108 
10109           /* Can get rid of this loop by storing field information in the global section */
10110           for (f2 = 0; f2 < f; ++f2) {
10111             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10112             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10113             poff += fdof - fcdof;
10114           }
10115           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10116           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10117           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10118         }
10119       }
10120     }
10121     PetscCall(ISRestoreIndices(spIS, &spmap));
10122     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10123     if (bs > 1) {
10124       /* We need to check that the block size does not come from non-contiguous fields */
10125       PetscInt i, j, set = 1;
10126       for (i = 0; i < subSize; i += bs) {
10127         for (j = 0; j < bs; ++j) {
10128           if (subIndices[i + j] != subIndices[i] + j) {
10129             set = 0;
10130             break;
10131           }
10132         }
10133       }
10134       if (set) PetscCall(ISSetBlockSize(*is, bs));
10135     }
10136     /* Attach nullspace */
10137     for (f = 0; f < Nf; ++f) {
10138       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10139       if ((*subdm)->nullspaceConstructors[f]) break;
10140     }
10141     if (f < Nf) {
10142       MatNullSpace nullSpace;
10143       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10144 
10145       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10146       PetscCall(MatNullSpaceDestroy(&nullSpace));
10147     }
10148   }
10149   PetscFunctionReturn(PETSC_SUCCESS);
10150 }
10151 
10152 /*@
10153   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10154 
10155   Input Parameters:
10156 + dm - The `DM`
10157 - dummy - unused argument
10158 
10159   Options Database Key:
10160 . -dm_plex_monitor_throughput - Activate the monitor
10161 
10162   Level: developer
10163 
10164 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10165 @*/
10166 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10167 {
10168 #if defined(PETSC_USE_LOG)
10169   PetscStageLog      stageLog;
10170   PetscLogEvent      event;
10171   PetscLogStage      stage;
10172   PetscEventPerfInfo eventInfo;
10173   PetscReal          cellRate, flopRate;
10174   PetscInt           cStart, cEnd, Nf, N;
10175   const char        *name;
10176 #endif
10177 
10178   PetscFunctionBegin;
10179   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10180 #if defined(PETSC_USE_LOG)
10181   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10182   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10183   PetscCall(DMGetNumFields(dm, &Nf));
10184   PetscCall(PetscLogGetStageLog(&stageLog));
10185   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10186   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10187   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10188   N        = (cEnd - cStart) * Nf * eventInfo.count;
10189   flopRate = eventInfo.flops / eventInfo.time;
10190   cellRate = N / eventInfo.time;
10191   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)));
10192 #else
10193   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10194 #endif
10195   PetscFunctionReturn(PETSC_SUCCESS);
10196 }
10197