xref: /petsc/src/dm/impls/plex/plex.c (revision 4b955ea4ba7f490d0e5a0cddf35875088abbd11f)
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;
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(0);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(0);
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(0);
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(0);
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(0);
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(0);
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(0);
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(0);
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(0);
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(0);
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(0);
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(0);
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(0);
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(DMGetGlobalVector(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(DMRestoreGlobalVector(dm, &v));
626   PetscFunctionReturn(0);
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(0);
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(0);
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(DMGetGlobalVector(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(DMRestoreGlobalVector(dm, &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(0);
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(0);
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(0);
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(DMGetCoordinateDM(dm, &cdm));
829   PetscCall(DMGetCoordinateSection(dm, &coordSection));
830   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
831   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
832   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
833   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
834   PetscCall(PetscViewerGetFormat(viewer, &format));
835   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
836     const char *name;
837     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
838     PetscInt    pStart, pEnd, p, numLabels, l;
839     PetscMPIInt rank, size;
840 
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     PetscCall(PetscViewerFlush(viewer));
945   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
946     const char  *name, *color;
947     const char  *defcolors[3]  = {"gray", "orange", "green"};
948     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
949     char         lname[PETSC_MAX_PATH_LEN];
950     PetscReal    scale      = 2.0;
951     PetscReal    tikzscale  = 1.0;
952     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
953     double       tcoords[3];
954     PetscScalar *coords;
955     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
956     PetscMPIInt  rank, size;
957     char       **names, **colors, **lcolors;
958     PetscBool    flg, lflg;
959     PetscBT      wp = NULL;
960     PetscInt     pEnd, pStart;
961 
962     PetscCall(DMGetDimension(dm, &dim));
963     PetscCall(DMPlexGetDepth(dm, &depth));
964     PetscCall(DMGetNumLabels(dm, &numLabels));
965     numLabels  = PetscMax(numLabels, 10);
966     numColors  = 10;
967     numLColors = 10;
968     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
969     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
970     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
971     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
972     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
973     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
974     n = 4;
975     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
976     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
977     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
978     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
979     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
980     if (!useLabels) numLabels = 0;
981     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
982     if (!useColors) {
983       numColors = 3;
984       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
985     }
986     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
987     if (!useColors) {
988       numLColors = 4;
989       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
990     }
991     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
992     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
993     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
994     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
995     if (depth < dim) plotEdges = PETSC_FALSE;
996     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
997 
998     /* filter points with labelvalue != labeldefaultvalue */
999     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1000     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1001     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1002     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1003     if (lflg) {
1004       DMLabel lbl;
1005 
1006       PetscCall(DMGetLabel(dm, lname, &lbl));
1007       if (lbl) {
1008         PetscInt val, defval;
1009 
1010         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1011         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1012         for (c = pStart; c < pEnd; c++) {
1013           PetscInt *closure = NULL;
1014           PetscInt  closureSize;
1015 
1016           PetscCall(DMLabelGetValue(lbl, c, &val));
1017           if (val == defval) continue;
1018 
1019           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1020           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1021           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1022         }
1023       }
1024     }
1025 
1026     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1027     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1028     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1029     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1030 \\documentclass[tikz]{standalone}\n\n\
1031 \\usepackage{pgflibraryshapes}\n\
1032 \\usetikzlibrary{backgrounds}\n\
1033 \\usetikzlibrary{arrows}\n\
1034 \\begin{document}\n"));
1035     if (size > 1) {
1036       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1037       for (p = 0; p < size; ++p) {
1038         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1039         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1040       }
1041       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1042     }
1043     if (drawHasse) {
1044       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1045 
1046       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1047       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1058     }
1059     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1060 
1061     /* Plot vertices */
1062     PetscCall(VecGetArray(coordinates, &coords));
1063     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1064     for (v = vStart; v < vEnd; ++v) {
1065       PetscInt  off, dof, d;
1066       PetscBool isLabeled = PETSC_FALSE;
1067 
1068       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1069       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1070       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1071       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1072       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1073       for (d = 0; d < dof; ++d) {
1074         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1075         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1076       }
1077       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1078       if (dim == 3) {
1079         PetscReal tmp = tcoords[1];
1080         tcoords[1]    = tcoords[2];
1081         tcoords[2]    = -tmp;
1082       }
1083       for (d = 0; d < dof; ++d) {
1084         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1085         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1086       }
1087       if (drawHasse) color = colors[0 % numColors];
1088       else color = colors[rank % numColors];
1089       for (l = 0; l < numLabels; ++l) {
1090         PetscInt val;
1091         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1092         if (val >= 0) {
1093           color     = lcolors[l % numLColors];
1094           isLabeled = PETSC_TRUE;
1095           break;
1096         }
1097       }
1098       if (drawNumbers[0]) {
1099         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1100       } else if (drawColors[0]) {
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1102       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1103     }
1104     PetscCall(VecRestoreArray(coordinates, &coords));
1105     PetscCall(PetscViewerFlush(viewer));
1106     /* Plot edges */
1107     if (plotEdges) {
1108       PetscCall(VecGetArray(coordinates, &coords));
1109       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1110       for (e = eStart; e < eEnd; ++e) {
1111         const PetscInt *cone;
1112         PetscInt        coneSize, offA, offB, dof, d;
1113 
1114         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1115         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1116         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1117         PetscCall(DMPlexGetCone(dm, e, &cone));
1118         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1119         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1120         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1121         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1122         for (d = 0; d < dof; ++d) {
1123           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1124           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1125         }
1126         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1127         if (dim == 3) {
1128           PetscReal tmp = tcoords[1];
1129           tcoords[1]    = tcoords[2];
1130           tcoords[2]    = -tmp;
1131         }
1132         for (d = 0; d < dof; ++d) {
1133           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1134           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1135         }
1136         if (drawHasse) color = colors[1 % numColors];
1137         else color = colors[rank % numColors];
1138         for (l = 0; l < numLabels; ++l) {
1139           PetscInt val;
1140           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1141           if (val >= 0) {
1142             color = lcolors[l % numLColors];
1143             break;
1144           }
1145         }
1146         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1147       }
1148       PetscCall(VecRestoreArray(coordinates, &coords));
1149       PetscCall(PetscViewerFlush(viewer));
1150       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1151     }
1152     /* Plot cells */
1153     if (dim == 3 || !drawNumbers[1]) {
1154       for (e = eStart; e < eEnd; ++e) {
1155         const PetscInt *cone;
1156 
1157         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1158         color = colors[rank % numColors];
1159         for (l = 0; l < numLabels; ++l) {
1160           PetscInt val;
1161           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1162           if (val >= 0) {
1163             color = lcolors[l % numLColors];
1164             break;
1165           }
1166         }
1167         PetscCall(DMPlexGetCone(dm, e, &cone));
1168         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1169       }
1170     } else {
1171       DMPolytopeType ct;
1172 
1173       /* Drawing a 2D polygon */
1174       for (c = cStart; c < cEnd; ++c) {
1175         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1176         PetscCall(DMPlexGetCellType(dm, c, &ct));
1177         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1178           const PetscInt *cone;
1179           PetscInt        coneSize, e;
1180 
1181           PetscCall(DMPlexGetCone(dm, c, &cone));
1182           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1183           for (e = 0; e < coneSize; ++e) {
1184             const PetscInt *econe;
1185 
1186             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1187             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));
1188           }
1189         } else {
1190           PetscInt *closure = NULL;
1191           PetscInt  closureSize, Nv = 0, v;
1192 
1193           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1194           for (p = 0; p < closureSize * 2; p += 2) {
1195             const PetscInt point = closure[p];
1196 
1197             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1198           }
1199           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1200           for (v = 0; v <= Nv; ++v) {
1201             const PetscInt vertex = closure[v % Nv];
1202 
1203             if (v > 0) {
1204               if (plotEdges) {
1205                 const PetscInt *edge;
1206                 PetscInt        endpoints[2], ne;
1207 
1208                 endpoints[0] = closure[v - 1];
1209                 endpoints[1] = vertex;
1210                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1211                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1212                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1213                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1214               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1215             }
1216             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1217           }
1218           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1219           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1220         }
1221       }
1222     }
1223     for (c = cStart; c < cEnd; ++c) {
1224       double             ccoords[3] = {0.0, 0.0, 0.0};
1225       PetscBool          isLabeled  = PETSC_FALSE;
1226       PetscScalar       *cellCoords = NULL;
1227       const PetscScalar *array;
1228       PetscInt           numCoords, cdim, d;
1229       PetscBool          isDG;
1230 
1231       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1232       PetscCall(DMGetCoordinateDim(dm, &cdim));
1233       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1234       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1235       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1236       for (p = 0; p < numCoords / cdim; ++p) {
1237         for (d = 0; d < cdim; ++d) {
1238           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1239           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1240         }
1241         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1242         if (cdim == 3) {
1243           PetscReal tmp = tcoords[1];
1244           tcoords[1]    = tcoords[2];
1245           tcoords[2]    = -tmp;
1246         }
1247         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1248       }
1249       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1250       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1251       for (d = 0; d < cdim; ++d) {
1252         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1253         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1254       }
1255       if (drawHasse) color = colors[depth % numColors];
1256       else color = colors[rank % numColors];
1257       for (l = 0; l < numLabels; ++l) {
1258         PetscInt val;
1259         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1260         if (val >= 0) {
1261           color     = lcolors[l % numLColors];
1262           isLabeled = PETSC_TRUE;
1263           break;
1264         }
1265       }
1266       if (drawNumbers[dim]) {
1267         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1268       } else if (drawColors[dim]) {
1269         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1270       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1271     }
1272     if (drawHasse) {
1273       color = colors[depth % numColors];
1274       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1275       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1277       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1279 
1280       color = colors[1 % numColors];
1281       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1282       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1286 
1287       color = colors[0 % numColors];
1288       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1289       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1293 
1294       for (p = pStart; p < pEnd; ++p) {
1295         const PetscInt *cone;
1296         PetscInt        coneSize, cp;
1297 
1298         PetscCall(DMPlexGetCone(dm, p, &cone));
1299         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1300         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1301       }
1302     }
1303     PetscCall(PetscViewerFlush(viewer));
1304     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1305     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1306     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1307     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1308     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1309     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1310     PetscCall(PetscFree3(names, colors, lcolors));
1311     PetscCall(PetscBTDestroy(&wp));
1312   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1313     Vec                    cown, acown;
1314     VecScatter             sct;
1315     ISLocalToGlobalMapping g2l;
1316     IS                     gid, acis;
1317     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1318     MPI_Group              ggroup, ngroup;
1319     PetscScalar           *array, nid;
1320     const PetscInt        *idxs;
1321     PetscInt              *idxs2, *start, *adjacency, *work;
1322     PetscInt64             lm[3], gm[3];
1323     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1324     PetscMPIInt            d1, d2, rank;
1325 
1326     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1327     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1328 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1329     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1330 #endif
1331     if (ncomm != MPI_COMM_NULL) {
1332       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1333       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1334       d1 = 0;
1335       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1336       nid = d2;
1337       PetscCallMPI(MPI_Group_free(&ggroup));
1338       PetscCallMPI(MPI_Group_free(&ngroup));
1339       PetscCallMPI(MPI_Comm_free(&ncomm));
1340     } else nid = 0.0;
1341 
1342     /* Get connectivity */
1343     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1344     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1345 
1346     /* filter overlapped local cells */
1347     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1348     PetscCall(ISGetIndices(gid, &idxs));
1349     PetscCall(ISGetLocalSize(gid, &cum));
1350     PetscCall(PetscMalloc1(cum, &idxs2));
1351     for (c = cStart, cum = 0; c < cEnd; c++) {
1352       if (idxs[c - cStart] < 0) continue;
1353       idxs2[cum++] = idxs[c - cStart];
1354     }
1355     PetscCall(ISRestoreIndices(gid, &idxs));
1356     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1357     PetscCall(ISDestroy(&gid));
1358     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1359 
1360     /* support for node-aware cell locality */
1361     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1362     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1363     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1364     PetscCall(VecGetArray(cown, &array));
1365     for (c = 0; c < numVertices; c++) array[c] = nid;
1366     PetscCall(VecRestoreArray(cown, &array));
1367     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1368     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1369     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1370     PetscCall(ISDestroy(&acis));
1371     PetscCall(VecScatterDestroy(&sct));
1372     PetscCall(VecDestroy(&cown));
1373 
1374     /* compute edgeCut */
1375     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1376     PetscCall(PetscMalloc1(cum, &work));
1377     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1378     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1379     PetscCall(ISDestroy(&gid));
1380     PetscCall(VecGetArray(acown, &array));
1381     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1382       PetscInt totl;
1383 
1384       totl = start[c + 1] - start[c];
1385       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1386       for (i = 0; i < totl; i++) {
1387         if (work[i] < 0) {
1388           ect += 1;
1389           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1390         }
1391       }
1392     }
1393     PetscCall(PetscFree(work));
1394     PetscCall(VecRestoreArray(acown, &array));
1395     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1396     lm[1] = -numVertices;
1397     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1398     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1399     lm[0] = ect;                     /* edgeCut */
1400     lm[1] = ectn;                    /* node-aware edgeCut */
1401     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1402     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1403     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1404 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1405     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1406 #else
1407     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1408 #endif
1409     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1410     PetscCall(PetscFree(start));
1411     PetscCall(PetscFree(adjacency));
1412     PetscCall(VecDestroy(&acown));
1413   } else {
1414     const char    *name;
1415     PetscInt      *sizes, *hybsizes, *ghostsizes;
1416     PetscInt       locDepth, depth, cellHeight, dim, d;
1417     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1418     PetscInt       numLabels, l, maxSize = 17;
1419     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1420     MPI_Comm       comm;
1421     PetscMPIInt    size, rank;
1422 
1423     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1424     PetscCallMPI(MPI_Comm_size(comm, &size));
1425     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1426     PetscCall(DMGetDimension(dm, &dim));
1427     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1428     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1429     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1430     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1431     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1432     PetscCall(DMPlexGetDepth(dm, &locDepth));
1433     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1434     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1435     gcNum = gcEnd - gcStart;
1436     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1437     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1438     for (d = 0; d <= depth; d++) {
1439       PetscInt Nc[2] = {0, 0}, ict;
1440 
1441       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1442       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1443       ict = ct0;
1444       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1445       ct0 = (DMPolytopeType)ict;
1446       for (p = pStart; p < pEnd; ++p) {
1447         DMPolytopeType ct;
1448 
1449         PetscCall(DMPlexGetCellType(dm, p, &ct));
1450         if (ct == ct0) ++Nc[0];
1451         else ++Nc[1];
1452       }
1453       if (size < maxSize) {
1454         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1455         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1456         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1457         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1458         for (p = 0; p < size; ++p) {
1459           if (rank == 0) {
1460             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1461             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1462             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1463           }
1464         }
1465       } else {
1466         PetscInt locMinMax[2];
1467 
1468         locMinMax[0] = Nc[0] + Nc[1];
1469         locMinMax[1] = Nc[0] + Nc[1];
1470         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1471         locMinMax[0] = Nc[1];
1472         locMinMax[1] = Nc[1];
1473         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1474         if (d == depth) {
1475           locMinMax[0] = gcNum;
1476           locMinMax[1] = gcNum;
1477           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1478         }
1479         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1480         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1481         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1482         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1483       }
1484       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1485     }
1486     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1487     {
1488       const PetscReal *maxCell;
1489       const PetscReal *L;
1490       PetscBool        localized;
1491 
1492       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1493       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1494       if (L || localized) {
1495         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1496         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1497         if (L) {
1498           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1499           for (d = 0; d < dim; ++d) {
1500             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1501             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1502           }
1503           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1504         }
1505         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1506         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1507       }
1508     }
1509     PetscCall(DMGetNumLabels(dm, &numLabels));
1510     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1511     for (l = 0; l < numLabels; ++l) {
1512       DMLabel         label;
1513       const char     *name;
1514       IS              valueIS;
1515       const PetscInt *values;
1516       PetscInt        numValues, v;
1517 
1518       PetscCall(DMGetLabelName(dm, l, &name));
1519       PetscCall(DMGetLabel(dm, name, &label));
1520       PetscCall(DMLabelGetNumValues(label, &numValues));
1521       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1522       PetscCall(DMLabelGetValueIS(label, &valueIS));
1523       PetscCall(ISGetIndices(valueIS, &values));
1524       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1525       for (v = 0; v < numValues; ++v) {
1526         PetscInt size;
1527 
1528         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1529         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1530         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1531       }
1532       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1533       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1534       PetscCall(ISRestoreIndices(valueIS, &values));
1535       PetscCall(ISDestroy(&valueIS));
1536     }
1537     {
1538       char    **labelNames;
1539       PetscInt  Nl = numLabels;
1540       PetscBool flg;
1541 
1542       PetscCall(PetscMalloc1(Nl, &labelNames));
1543       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1544       for (l = 0; l < Nl; ++l) {
1545         DMLabel label;
1546 
1547         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1548         if (flg) {
1549           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1550           PetscCall(DMLabelView(label, viewer));
1551         }
1552         PetscCall(PetscFree(labelNames[l]));
1553       }
1554       PetscCall(PetscFree(labelNames));
1555     }
1556     /* If no fields are specified, people do not want to see adjacency */
1557     if (dm->Nf) {
1558       PetscInt f;
1559 
1560       for (f = 0; f < dm->Nf; ++f) {
1561         const char *name;
1562 
1563         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1564         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1565         PetscCall(PetscViewerASCIIPushTab(viewer));
1566         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1567         if (dm->fields[f].adjacency[0]) {
1568           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1569           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1570         } else {
1571           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1572           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1573         }
1574         PetscCall(PetscViewerASCIIPopTab(viewer));
1575       }
1576     }
1577     PetscCall(DMGetCoarseDM(dm, &cdm));
1578     if (cdm) {
1579       PetscCall(PetscViewerASCIIPushTab(viewer));
1580       PetscCall(DMPlexView_Ascii(cdm, viewer));
1581       PetscCall(PetscViewerASCIIPopTab(viewer));
1582     }
1583   }
1584   PetscFunctionReturn(0);
1585 }
1586 
1587 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1588 {
1589   DMPolytopeType ct;
1590   PetscMPIInt    rank;
1591   PetscInt       cdim;
1592 
1593   PetscFunctionBegin;
1594   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1595   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1596   PetscCall(DMGetCoordinateDim(dm, &cdim));
1597   switch (ct) {
1598   case DM_POLYTOPE_SEGMENT:
1599   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1600     switch (cdim) {
1601     case 1: {
1602       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1603       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1604 
1605       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1606       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1607       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1608     } break;
1609     case 2: {
1610       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1611       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1612       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1613 
1614       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1615       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));
1616       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));
1617     } break;
1618     default:
1619       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1620     }
1621     break;
1622   case DM_POLYTOPE_TRIANGLE:
1623     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));
1624     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1625     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1626     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1627     break;
1628   case DM_POLYTOPE_QUADRILATERAL:
1629     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));
1630     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));
1631     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1632     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1633     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1635     break;
1636   case DM_POLYTOPE_FV_GHOST:
1637     break;
1638   default:
1639     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1640   }
1641   PetscFunctionReturn(0);
1642 }
1643 
1644 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1645 {
1646   DMPolytopeType ct;
1647   PetscReal      centroid[2] = {0., 0.};
1648   PetscMPIInt    rank;
1649   PetscInt       fillColor, v, e, d;
1650 
1651   PetscFunctionBegin;
1652   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1653   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1654   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1655   switch (ct) {
1656   case DM_POLYTOPE_TRIANGLE: {
1657     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1658 
1659     for (v = 0; v < 3; ++v) {
1660       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1661       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1662     }
1663     for (e = 0; e < 3; ++e) {
1664       refCoords[0] = refVertices[e * 2 + 0];
1665       refCoords[1] = refVertices[e * 2 + 1];
1666       for (d = 1; d <= edgeDiv; ++d) {
1667         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1668         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1669       }
1670       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1671       for (d = 0; d < edgeDiv; ++d) {
1672         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));
1673         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1674       }
1675     }
1676   } break;
1677   default:
1678     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1679   }
1680   PetscFunctionReturn(0);
1681 }
1682 
1683 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1684 {
1685   PetscDraw          draw;
1686   DM                 cdm;
1687   PetscSection       coordSection;
1688   Vec                coordinates;
1689   const PetscScalar *coords;
1690   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1691   PetscReal         *refCoords, *edgeCoords;
1692   PetscBool          isnull, drawAffine = PETSC_TRUE;
1693   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1694 
1695   PetscFunctionBegin;
1696   PetscCall(DMGetCoordinateDim(dm, &dim));
1697   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1698   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1699   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1700   PetscCall(DMGetCoordinateDM(dm, &cdm));
1701   PetscCall(DMGetLocalSection(cdm, &coordSection));
1702   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1703   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1704   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1705 
1706   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1707   PetscCall(PetscDrawIsNull(draw, &isnull));
1708   if (isnull) PetscFunctionReturn(0);
1709   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1710 
1711   PetscCall(VecGetLocalSize(coordinates, &N));
1712   PetscCall(VecGetArrayRead(coordinates, &coords));
1713   for (c = 0; c < N; c += dim) {
1714     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1715     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1716     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1717     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1718   }
1719   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1720   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1721   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1722   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1723   PetscCall(PetscDrawClear(draw));
1724 
1725   for (c = cStart; c < cEnd; ++c) {
1726     PetscScalar *coords = NULL;
1727     PetscInt     numCoords;
1728 
1729     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1730     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1731     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1732     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1733   }
1734   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1735   PetscCall(PetscDrawFlush(draw));
1736   PetscCall(PetscDrawPause(draw));
1737   PetscCall(PetscDrawSave(draw));
1738   PetscFunctionReturn(0);
1739 }
1740 
1741 #if defined(PETSC_HAVE_EXODUSII)
1742   #include <exodusII.h>
1743   #include <petscviewerexodusii.h>
1744 #endif
1745 
1746 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1747 {
1748   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1749   char      name[PETSC_MAX_PATH_LEN];
1750 
1751   PetscFunctionBegin;
1752   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1753   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1754   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1755   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1756   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1757   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1759   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1761   if (iascii) {
1762     PetscViewerFormat format;
1763     PetscCall(PetscViewerGetFormat(viewer, &format));
1764     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1765     else PetscCall(DMPlexView_Ascii(dm, viewer));
1766   } else if (ishdf5) {
1767 #if defined(PETSC_HAVE_HDF5)
1768     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1769 #else
1770     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1771 #endif
1772   } else if (isvtk) {
1773     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1774   } else if (isdraw) {
1775     PetscCall(DMPlexView_Draw(dm, viewer));
1776   } else if (isglvis) {
1777     PetscCall(DMPlexView_GLVis(dm, viewer));
1778 #if defined(PETSC_HAVE_EXODUSII)
1779   } else if (isexodus) {
1780     /*
1781       exodusII requires that all sets be part of exactly one cell set.
1782       If the dm does not have a "Cell Sets" label defined, we create one
1783       with ID 1, containig all cells.
1784       Note that if the Cell Sets label is defined but does not cover all cells,
1785       we may still have a problem. This should probably be checked here or in the viewer;
1786     */
1787     PetscInt numCS;
1788     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1789     if (!numCS) {
1790       PetscInt cStart, cEnd, c;
1791       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1792       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1793       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1794     }
1795     PetscCall(DMView_PlexExodusII(dm, viewer));
1796 #endif
1797 #if defined(PETSC_HAVE_CGNS)
1798   } else if (iscgns) {
1799     PetscCall(DMView_PlexCGNS(dm, viewer));
1800 #endif
1801   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1802   /* Optionally view the partition */
1803   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1804   if (flg) {
1805     Vec ranks;
1806     PetscCall(DMPlexCreateRankField(dm, &ranks));
1807     PetscCall(VecView(ranks, viewer));
1808     PetscCall(VecDestroy(&ranks));
1809   }
1810   /* Optionally view a label */
1811   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1812   if (flg) {
1813     DMLabel label;
1814     Vec     val;
1815 
1816     PetscCall(DMGetLabel(dm, name, &label));
1817     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1818     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1819     PetscCall(VecView(val, viewer));
1820     PetscCall(VecDestroy(&val));
1821   }
1822   PetscFunctionReturn(0);
1823 }
1824 
1825 /*@
1826   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1827 
1828   Collective on dm
1829 
1830   Input Parameters:
1831 + dm     - The `DM` whose topology is to be saved
1832 - viewer - The `PetscViewer` to save it in
1833 
1834   Level: advanced
1835 
1836 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1837 @*/
1838 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1839 {
1840   PetscBool ishdf5;
1841 
1842   PetscFunctionBegin;
1843   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1844   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1845   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1846   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1847   if (ishdf5) {
1848 #if defined(PETSC_HAVE_HDF5)
1849     PetscViewerFormat format;
1850     PetscCall(PetscViewerGetFormat(viewer, &format));
1851     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1852       IS globalPointNumbering;
1853 
1854       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1855       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1856       PetscCall(ISDestroy(&globalPointNumbering));
1857     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1858 #else
1859     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1860 #endif
1861   }
1862   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1863   PetscFunctionReturn(0);
1864 }
1865 
1866 /*@
1867   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1868 
1869   Collective on dm
1870 
1871   Input Parameters:
1872 + dm     - The `DM` whose coordinates are to be saved
1873 - viewer - The `PetscViewer` for saving
1874 
1875   Level: advanced
1876 
1877 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1878 @*/
1879 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1880 {
1881   PetscBool ishdf5;
1882 
1883   PetscFunctionBegin;
1884   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1885   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1886   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1887   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1888   if (ishdf5) {
1889 #if defined(PETSC_HAVE_HDF5)
1890     PetscViewerFormat format;
1891     PetscCall(PetscViewerGetFormat(viewer, &format));
1892     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1893       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1894     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1895 #else
1896     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1897 #endif
1898   }
1899   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1900   PetscFunctionReturn(0);
1901 }
1902 
1903 /*@
1904   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1905 
1906   Collective on dm
1907 
1908   Input Parameters:
1909 + dm     - The `DM` whose labels are to be saved
1910 - viewer - The `PetscViewer` for saving
1911 
1912   Level: advanced
1913 
1914 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1915 @*/
1916 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1917 {
1918   PetscBool ishdf5;
1919 
1920   PetscFunctionBegin;
1921   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1922   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1923   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1924   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1925   if (ishdf5) {
1926 #if defined(PETSC_HAVE_HDF5)
1927     IS                globalPointNumbering;
1928     PetscViewerFormat format;
1929 
1930     PetscCall(PetscViewerGetFormat(viewer, &format));
1931     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1932       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1933       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1934       PetscCall(ISDestroy(&globalPointNumbering));
1935     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1936 #else
1937     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1938 #endif
1939   }
1940   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1941   PetscFunctionReturn(0);
1942 }
1943 
1944 /*@
1945   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1946 
1947   Collective on dm
1948 
1949   Input Parameters:
1950 + dm         - The `DM` that contains the topology on which the section to be saved is defined
1951 . viewer     - The `PetscViewer` for saving
1952 - sectiondm  - The `DM` that contains the section to be saved
1953 
1954   Level: advanced
1955 
1956   Notes:
1957   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.
1958 
1959   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.
1960 
1961 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1962 @*/
1963 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1964 {
1965   PetscBool ishdf5;
1966 
1967   PetscFunctionBegin;
1968   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1969   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1970   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1971   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1972   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1973   if (ishdf5) {
1974 #if defined(PETSC_HAVE_HDF5)
1975     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1976 #else
1977     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1978 #endif
1979   }
1980   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1981   PetscFunctionReturn(0);
1982 }
1983 
1984 /*@
1985   DMPlexGlobalVectorView - Saves a global vector
1986 
1987   Collective on dm
1988 
1989   Input Parameters:
1990 + dm        - The `DM` that represents the topology
1991 . viewer    - The `PetscViewer` to save data with
1992 . sectiondm - The `DM` that contains the global section on which vec is defined
1993 - vec       - The global vector to be saved
1994 
1995   Level: advanced
1996 
1997   Notes:
1998   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.
1999 
2000   Typical calling sequence:
2001 .vb
2002        DMCreate(PETSC_COMM_WORLD, &dm);
2003        DMSetType(dm, DMPLEX);
2004        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2005        DMClone(dm, &sectiondm);
2006        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2007        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2008        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2009        PetscSectionSetChart(section, pStart, pEnd);
2010        PetscSectionSetUp(section);
2011        DMSetLocalSection(sectiondm, section);
2012        PetscSectionDestroy(&section);
2013        DMGetGlobalVector(sectiondm, &vec);
2014        PetscObjectSetName((PetscObject)vec, "vec_name");
2015        DMPlexTopologyView(dm, viewer);
2016        DMPlexSectionView(dm, viewer, sectiondm);
2017        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2018        DMRestoreGlobalVector(sectiondm, &vec);
2019        DMDestroy(&sectiondm);
2020        DMDestroy(&dm);
2021 .ve
2022 
2023 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2024 @*/
2025 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2026 {
2027   PetscBool ishdf5;
2028 
2029   PetscFunctionBegin;
2030   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2031   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2032   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2033   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2034   /* Check consistency */
2035   {
2036     PetscSection section;
2037     PetscBool    includesConstraints;
2038     PetscInt     m, m1;
2039 
2040     PetscCall(VecGetLocalSize(vec, &m1));
2041     PetscCall(DMGetGlobalSection(sectiondm, &section));
2042     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2043     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2044     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2045     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2046   }
2047   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2048   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2049   if (ishdf5) {
2050 #if defined(PETSC_HAVE_HDF5)
2051     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2052 #else
2053     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2054 #endif
2055   }
2056   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2057   PetscFunctionReturn(0);
2058 }
2059 
2060 /*@
2061   DMPlexLocalVectorView - Saves a local vector
2062 
2063   Collective on dm
2064 
2065   Input Parameters:
2066 + dm        - The `DM` that represents the topology
2067 . viewer    - The `PetscViewer` to save data with
2068 . sectiondm - The `DM` that contains the local section on which vec is defined; may be the same as dm
2069 - vec       - The local vector to be saved
2070 
2071   Level: advanced
2072 
2073   Note:
2074   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.
2075 
2076   Typical calling sequence:
2077 .vb
2078        DMCreate(PETSC_COMM_WORLD, &dm);
2079        DMSetType(dm, DMPLEX);
2080        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2081        DMClone(dm, &sectiondm);
2082        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2083        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2084        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2085        PetscSectionSetChart(section, pStart, pEnd);
2086        PetscSectionSetUp(section);
2087        DMSetLocalSection(sectiondm, section);
2088        DMGetLocalVector(sectiondm, &vec);
2089        PetscObjectSetName((PetscObject)vec, "vec_name");
2090        DMPlexTopologyView(dm, viewer);
2091        DMPlexSectionView(dm, viewer, sectiondm);
2092        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2093        DMRestoreLocalVector(sectiondm, &vec);
2094        DMDestroy(&sectiondm);
2095        DMDestroy(&dm);
2096 .ve
2097 
2098 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2099 @*/
2100 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2101 {
2102   PetscBool ishdf5;
2103 
2104   PetscFunctionBegin;
2105   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2106   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2107   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2108   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2109   /* Check consistency */
2110   {
2111     PetscSection section;
2112     PetscBool    includesConstraints;
2113     PetscInt     m, m1;
2114 
2115     PetscCall(VecGetLocalSize(vec, &m1));
2116     PetscCall(DMGetLocalSection(sectiondm, &section));
2117     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2118     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2119     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2120     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2121   }
2122   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2123   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2124   if (ishdf5) {
2125 #if defined(PETSC_HAVE_HDF5)
2126     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2127 #else
2128     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2129 #endif
2130   }
2131   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2132   PetscFunctionReturn(0);
2133 }
2134 
2135 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2136 {
2137   PetscBool ishdf5;
2138 
2139   PetscFunctionBegin;
2140   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2141   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2142   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2143   if (ishdf5) {
2144 #if defined(PETSC_HAVE_HDF5)
2145     PetscViewerFormat format;
2146     PetscCall(PetscViewerGetFormat(viewer, &format));
2147     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2148       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2149     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2150       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2151     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2152     PetscFunctionReturn(0);
2153 #else
2154     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2155 #endif
2156   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2157 }
2158 
2159 /*@
2160   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2161 
2162   Collective on dm
2163 
2164   Input Parameters:
2165 + dm                - The `DM` into which the topology is loaded
2166 - viewer            - The `PetscViewer` for the saved topology
2167 
2168   Output Parameters:
2169 . 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
2170 
2171   Level: advanced
2172 
2173 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2174           `PetscViewer`, `PetscSF`
2175 @*/
2176 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2177 {
2178   PetscBool ishdf5;
2179 
2180   PetscFunctionBegin;
2181   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2182   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2183   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2184   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2185   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2186   if (ishdf5) {
2187 #if defined(PETSC_HAVE_HDF5)
2188     PetscViewerFormat format;
2189     PetscCall(PetscViewerGetFormat(viewer, &format));
2190     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2191       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2192     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2193 #else
2194     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2195 #endif
2196   }
2197   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2198   PetscFunctionReturn(0);
2199 }
2200 
2201 /*@
2202   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2203 
2204   Collective on dm
2205 
2206   Input Parameters:
2207 + dm     - The `DM` into which the coordinates are loaded
2208 . viewer - The `PetscViewer` for the saved coordinates
2209 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2210 
2211   Level: advanced
2212 
2213 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2214           `PetscSF`, `PetscViewer`
2215 @*/
2216 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2217 {
2218   PetscBool ishdf5;
2219 
2220   PetscFunctionBegin;
2221   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2222   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2223   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2224   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2225   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2226   if (ishdf5) {
2227 #if defined(PETSC_HAVE_HDF5)
2228     PetscViewerFormat format;
2229     PetscCall(PetscViewerGetFormat(viewer, &format));
2230     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2231       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2232     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2233 #else
2234     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2235 #endif
2236   }
2237   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2238   PetscFunctionReturn(0);
2239 }
2240 
2241 /*@
2242   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2243 
2244   Collective on dm
2245 
2246   Input Parameters:
2247 + dm     - The `DM` into which the labels are loaded
2248 . viewer - The `PetscViewer` for the saved labels
2249 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2250 
2251   Level: advanced
2252 
2253   Note:
2254   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2255 
2256 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2257           `PetscSF`, `PetscViewer`
2258 @*/
2259 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2260 {
2261   PetscBool ishdf5;
2262 
2263   PetscFunctionBegin;
2264   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2265   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2266   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2267   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2268   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2269   if (ishdf5) {
2270 #if defined(PETSC_HAVE_HDF5)
2271     PetscViewerFormat format;
2272 
2273     PetscCall(PetscViewerGetFormat(viewer, &format));
2274     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2275       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2276     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2277 #else
2278     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2279 #endif
2280   }
2281   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2282   PetscFunctionReturn(0);
2283 }
2284 
2285 /*@
2286   DMPlexSectionLoad - Loads section into a `DMPLEX`
2287 
2288   Collective on dm
2289 
2290   Input Parameters:
2291 + dm          - The `DM` that represents the topology
2292 . viewer      - The `PetscViewer` that represents the on-disk section (sectionA)
2293 . sectiondm   - The `DM` into which the on-disk section (sectionA) is migrated
2294 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2295 
2296   Output Parameters
2297 + 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)
2298 - 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)
2299 
2300   Level: advanced
2301 
2302   Notes:
2303   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.
2304 
2305   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.
2306 
2307   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.
2308 
2309   Example using 2 processes:
2310 .vb
2311   NX (number of points on dm): 4
2312   sectionA                   : the on-disk section
2313   vecA                       : a vector associated with sectionA
2314   sectionB                   : sectiondm's local section constructed in this function
2315   vecB (local)               : a vector associated with sectiondm's local section
2316   vecB (global)              : a vector associated with sectiondm's global section
2317 
2318                                      rank 0    rank 1
2319   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2320   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2321   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2322   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2323   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2324   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2325   sectionB->atlasDof             :     1 0 1 | 1 3
2326   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2327   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2328   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2329 .ve
2330   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2331 
2332 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2333 @*/
2334 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2335 {
2336   PetscBool ishdf5;
2337 
2338   PetscFunctionBegin;
2339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2340   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2341   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2342   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2343   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2344   if (localDofSF) PetscValidPointer(localDofSF, 6);
2345   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2346   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2347   if (ishdf5) {
2348 #if defined(PETSC_HAVE_HDF5)
2349     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2350 #else
2351     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2352 #endif
2353   }
2354   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2355   PetscFunctionReturn(0);
2356 }
2357 
2358 /*@
2359   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2360 
2361   Collective on dm
2362 
2363   Input Parameters:
2364 + dm        - The `DM` that represents the topology
2365 . viewer    - The `PetscViewer` that represents the on-disk vector data
2366 . sectiondm - The `DM` that contains the global section on which vec is defined
2367 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2368 - vec       - The global vector to set values of
2369 
2370   Level: advanced
2371 
2372   Notes:
2373   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.
2374 
2375   Typical calling sequence:
2376 .vb
2377        DMCreate(PETSC_COMM_WORLD, &dm);
2378        DMSetType(dm, DMPLEX);
2379        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2380        DMPlexTopologyLoad(dm, viewer, &sfX);
2381        DMClone(dm, &sectiondm);
2382        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2383        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2384        DMGetGlobalVector(sectiondm, &vec);
2385        PetscObjectSetName((PetscObject)vec, "vec_name");
2386        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2387        DMRestoreGlobalVector(sectiondm, &vec);
2388        PetscSFDestroy(&gsf);
2389        PetscSFDestroy(&sfX);
2390        DMDestroy(&sectiondm);
2391        DMDestroy(&dm);
2392 .ve
2393 
2394 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2395           `PetscSF`, `PetscViewer`
2396 @*/
2397 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2398 {
2399   PetscBool ishdf5;
2400 
2401   PetscFunctionBegin;
2402   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2403   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2404   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2405   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2406   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2407   /* Check consistency */
2408   {
2409     PetscSection section;
2410     PetscBool    includesConstraints;
2411     PetscInt     m, m1;
2412 
2413     PetscCall(VecGetLocalSize(vec, &m1));
2414     PetscCall(DMGetGlobalSection(sectiondm, &section));
2415     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2416     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2417     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2418     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2419   }
2420   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2421   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2422   if (ishdf5) {
2423 #if defined(PETSC_HAVE_HDF5)
2424     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2425 #else
2426     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2427 #endif
2428   }
2429   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2430   PetscFunctionReturn(0);
2431 }
2432 
2433 /*@
2434   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2435 
2436   Collective on dm
2437 
2438   Input Parameters:
2439 + dm        - The `DM` that represents the topology
2440 . viewer    - The `PetscViewer` that represents the on-disk vector data
2441 . sectiondm - The `DM` that contains the local section on which vec is defined
2442 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2443 - vec       - The local vector to set values of
2444 
2445   Level: advanced
2446 
2447   Notes:
2448   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.
2449 
2450   Typical calling sequence:
2451 .vb
2452        DMCreate(PETSC_COMM_WORLD, &dm);
2453        DMSetType(dm, DMPLEX);
2454        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2455        DMPlexTopologyLoad(dm, viewer, &sfX);
2456        DMClone(dm, &sectiondm);
2457        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2458        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2459        DMGetLocalVector(sectiondm, &vec);
2460        PetscObjectSetName((PetscObject)vec, "vec_name");
2461        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2462        DMRestoreLocalVector(sectiondm, &vec);
2463        PetscSFDestroy(&lsf);
2464        PetscSFDestroy(&sfX);
2465        DMDestroy(&sectiondm);
2466        DMDestroy(&dm);
2467 .ve
2468 
2469 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2470           `PetscSF`, `PetscViewer`
2471 @*/
2472 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2473 {
2474   PetscBool ishdf5;
2475 
2476   PetscFunctionBegin;
2477   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2478   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2479   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2480   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2481   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2482   /* Check consistency */
2483   {
2484     PetscSection section;
2485     PetscBool    includesConstraints;
2486     PetscInt     m, m1;
2487 
2488     PetscCall(VecGetLocalSize(vec, &m1));
2489     PetscCall(DMGetLocalSection(sectiondm, &section));
2490     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2491     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2492     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2493     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2494   }
2495   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2496   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2497   if (ishdf5) {
2498 #if defined(PETSC_HAVE_HDF5)
2499     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2500 #else
2501     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2502 #endif
2503   }
2504   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2505   PetscFunctionReturn(0);
2506 }
2507 
2508 PetscErrorCode DMDestroy_Plex(DM dm)
2509 {
2510   DM_Plex *mesh = (DM_Plex *)dm->data;
2511 
2512   PetscFunctionBegin;
2513   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2514   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2515   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2516   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2517   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2518   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2519   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2520   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2521   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2522   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2523   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2524   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2525   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2526   if (--mesh->refct > 0) PetscFunctionReturn(0);
2527   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2528   PetscCall(PetscFree(mesh->cones));
2529   PetscCall(PetscFree(mesh->coneOrientations));
2530   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2531   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2532   PetscCall(PetscFree(mesh->supports));
2533   PetscCall(PetscFree(mesh->facesTmp));
2534   PetscCall(PetscFree(mesh->tetgenOpts));
2535   PetscCall(PetscFree(mesh->triangleOpts));
2536   PetscCall(PetscFree(mesh->transformType));
2537   PetscCall(PetscFree(mesh->distributionName));
2538   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2539   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2540   PetscCall(ISDestroy(&mesh->subpointIS));
2541   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2542   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2543   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2544   PetscCall(ISDestroy(&mesh->anchorIS));
2545   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2546   PetscCall(PetscFree(mesh->parents));
2547   PetscCall(PetscFree(mesh->childIDs));
2548   PetscCall(PetscSectionDestroy(&mesh->childSection));
2549   PetscCall(PetscFree(mesh->children));
2550   PetscCall(DMDestroy(&mesh->referenceTree));
2551   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2552   PetscCall(PetscFree(mesh->neighbors));
2553   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2554   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2555   PetscCall(PetscFree(mesh));
2556   PetscFunctionReturn(0);
2557 }
2558 
2559 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2560 {
2561   PetscSection           sectionGlobal;
2562   PetscInt               bs = -1, mbs;
2563   PetscInt               localSize, localStart = 0;
2564   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2565   MatType                mtype;
2566   ISLocalToGlobalMapping ltog;
2567 
2568   PetscFunctionBegin;
2569   PetscCall(MatInitializePackage());
2570   mtype = dm->mattype;
2571   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2572   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2573   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2574   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2575   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2576   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2577   PetscCall(MatSetType(*J, mtype));
2578   PetscCall(MatSetFromOptions(*J));
2579   PetscCall(MatGetBlockSize(*J, &mbs));
2580   if (mbs > 1) bs = mbs;
2581   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2582   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2583   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2584   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2585   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2586   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2587   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2588   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2589   if (!isShell) {
2590     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2591     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2592     PetscInt  pStart, pEnd, p, dof, cdof;
2593 
2594     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2595 
2596     PetscCall(PetscCalloc1(localSize, &pblocks));
2597     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2598     for (p = pStart; p < pEnd; ++p) {
2599       PetscInt bdof, offset;
2600 
2601       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2602       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2603       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2604       for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2605       dof  = dof < 0 ? -(dof + 1) : dof;
2606       bdof = cdof && (dof - cdof) ? 1 : dof;
2607       if (dof) {
2608         if (bs < 0) {
2609           bs = bdof;
2610         } else if (bs != bdof) {
2611           bs = 1;
2612         }
2613       }
2614     }
2615     /* Must have same blocksize on all procs (some might have no points) */
2616     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2617     bsLocal[1] = bs;
2618     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2619     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2620     else bs = bsMinMax[0];
2621     bs = PetscMax(1, bs);
2622     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2623     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2624       PetscCall(MatSetBlockSize(*J, bs));
2625       PetscCall(MatSetUp(*J));
2626     } else {
2627       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2628       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2629       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2630     }
2631     { // Consolidate blocks
2632       PetscInt nblocks = 0;
2633       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2634         if (pblocks[i] == 0) continue;
2635         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2636         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]);
2637       }
2638       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2639     }
2640     PetscCall(PetscFree(pblocks));
2641   }
2642   PetscCall(MatSetDM(*J, dm));
2643   PetscFunctionReturn(0);
2644 }
2645 
2646 /*@
2647   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2648 
2649   Not Collective
2650 
2651   Input Parameter:
2652 . mesh - The `DMPLEX`
2653 
2654   Output Parameters:
2655 . subsection - The subdomain section
2656 
2657   Level: developer
2658 
2659 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `PetscSection`
2660 @*/
2661 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2662 {
2663   DM_Plex *mesh = (DM_Plex *)dm->data;
2664 
2665   PetscFunctionBegin;
2666   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2667   if (!mesh->subdomainSection) {
2668     PetscSection section;
2669     PetscSF      sf;
2670 
2671     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2672     PetscCall(DMGetLocalSection(dm, &section));
2673     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2674     PetscCall(PetscSFDestroy(&sf));
2675   }
2676   *subsection = mesh->subdomainSection;
2677   PetscFunctionReturn(0);
2678 }
2679 
2680 /*@
2681   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2682 
2683   Not Collective
2684 
2685   Input Parameter:
2686 . mesh - The `DMPLEX`
2687 
2688   Output Parameters:
2689 + pStart - The first mesh point
2690 - pEnd   - The upper bound for mesh points
2691 
2692   Level: beginner
2693 
2694 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2695 @*/
2696 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2697 {
2698   DM_Plex *mesh = (DM_Plex *)dm->data;
2699 
2700   PetscFunctionBegin;
2701   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2702   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2703   PetscFunctionReturn(0);
2704 }
2705 
2706 /*@
2707   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2708 
2709   Not Collective
2710 
2711   Input Parameters:
2712 + mesh - The `DMPLEX`
2713 . pStart - The first mesh point
2714 - pEnd   - The upper bound for mesh points
2715 
2716   Level: beginner
2717 
2718 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2719 @*/
2720 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2721 {
2722   DM_Plex *mesh = (DM_Plex *)dm->data;
2723 
2724   PetscFunctionBegin;
2725   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2726   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2727   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2728   PetscFunctionReturn(0);
2729 }
2730 
2731 /*@
2732   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2733 
2734   Not Collective
2735 
2736   Input Parameters:
2737 + mesh - The `DMPLEX`
2738 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2739 
2740   Output Parameter:
2741 . size - The cone size for point p
2742 
2743   Level: beginner
2744 
2745 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2746 @*/
2747 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2748 {
2749   DM_Plex *mesh = (DM_Plex *)dm->data;
2750 
2751   PetscFunctionBegin;
2752   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2753   PetscValidIntPointer(size, 3);
2754   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2755   PetscFunctionReturn(0);
2756 }
2757 
2758 /*@
2759   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2760 
2761   Not Collective
2762 
2763   Input Parameters:
2764 + mesh - The `DMPLEX`
2765 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2766 - size - The cone size for point p
2767 
2768   Level: beginner
2769 
2770   Note:
2771   This should be called after `DMPlexSetChart()`.
2772 
2773 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2774 @*/
2775 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2776 {
2777   DM_Plex *mesh = (DM_Plex *)dm->data;
2778 
2779   PetscFunctionBegin;
2780   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2781   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2782   PetscFunctionReturn(0);
2783 }
2784 
2785 /*@
2786   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2787 
2788   Not Collective
2789 
2790   Input Parameters:
2791 + mesh - The `DMPLEX`
2792 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2793 - size - The additional cone size for point p
2794 
2795   Level: beginner
2796 
2797   Note:
2798   This should be called after `DMPlexSetChart()`.
2799 
2800 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2801 @*/
2802 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size)
2803 {
2804   DM_Plex *mesh = (DM_Plex *)dm->data;
2805   PetscFunctionBegin;
2806   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2807   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2808   PetscFunctionReturn(0);
2809 }
2810 
2811 /*@C
2812   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2813 
2814   Not Collective
2815 
2816   Input Parameters:
2817 + dm - The `DMPLEX`
2818 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2819 
2820   Output Parameter:
2821 . cone - An array of points which are on the in-edges for point p
2822 
2823   Level: beginner
2824 
2825   Fortran Note:
2826   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2827   `DMPlexRestoreCone()` is not needed/available in C.
2828 
2829 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2830 @*/
2831 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2832 {
2833   DM_Plex *mesh = (DM_Plex *)dm->data;
2834   PetscInt off;
2835 
2836   PetscFunctionBegin;
2837   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2838   PetscValidPointer(cone, 3);
2839   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2840   *cone = &mesh->cones[off];
2841   PetscFunctionReturn(0);
2842 }
2843 
2844 /*@C
2845   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2846 
2847   Not Collective
2848 
2849   Input Parameters:
2850 + dm - The `DMPLEX`
2851 - p - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2852 
2853   Output Parameters:
2854 + pConesSection - `PetscSection` describing the layout of pCones
2855 - pCones - An array of points which are on the in-edges for the point set p
2856 
2857   Level: intermediate
2858 
2859 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2860 @*/
2861 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2862 {
2863   PetscSection cs, newcs;
2864   PetscInt    *cones;
2865   PetscInt    *newarr = NULL;
2866   PetscInt     n;
2867 
2868   PetscFunctionBegin;
2869   PetscCall(DMPlexGetCones(dm, &cones));
2870   PetscCall(DMPlexGetConeSection(dm, &cs));
2871   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2872   if (pConesSection) *pConesSection = newcs;
2873   if (pCones) {
2874     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2875     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2876   }
2877   PetscFunctionReturn(0);
2878 }
2879 
2880 /*@
2881   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2882 
2883   Not Collective
2884 
2885   Input Parameters:
2886 + dm - The `DMPLEX`
2887 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2888 
2889   Output Parameter:
2890 . expandedPoints - An array of vertices recursively expanded from input points
2891 
2892   Level: advanced
2893 
2894   Notes:
2895   Like `DMPlexGetConeRecursive()` but returns only the 0-depth IS (i.e. vertices only) and no sections.
2896 
2897   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2898 
2899 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2900           `DMPlexGetDepth()`, `IS`
2901 @*/
2902 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2903 {
2904   IS      *expandedPointsAll;
2905   PetscInt depth;
2906 
2907   PetscFunctionBegin;
2908   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2909   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2910   PetscValidPointer(expandedPoints, 3);
2911   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2912   *expandedPoints = expandedPointsAll[0];
2913   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2914   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2915   PetscFunctionReturn(0);
2916 }
2917 
2918 /*@
2919   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).
2920 
2921   Not Collective
2922 
2923   Input Parameters:
2924 + dm - The `DMPLEX`
2925 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2926 
2927   Output Parameters:
2928 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2929 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2930 - sections - (optional) An array of sections which describe mappings from points to their cone points
2931 
2932   Level: advanced
2933 
2934   Notes:
2935   Like `DMPlexGetConeTuple()` but recursive.
2936 
2937   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.
2938   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2939 
2940   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:
2941   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2942   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2943 
2944 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2945           `DMPlexGetDepth()`, `PetscSection`, `IS`
2946 @*/
2947 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2948 {
2949   const PetscInt *arr0 = NULL, *cone = NULL;
2950   PetscInt       *arr = NULL, *newarr = NULL;
2951   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2952   IS             *expandedPoints_;
2953   PetscSection   *sections_;
2954 
2955   PetscFunctionBegin;
2956   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2957   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2958   if (depth) PetscValidIntPointer(depth, 3);
2959   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2960   if (sections) PetscValidPointer(sections, 5);
2961   PetscCall(ISGetLocalSize(points, &n));
2962   PetscCall(ISGetIndices(points, &arr0));
2963   PetscCall(DMPlexGetDepth(dm, &depth_));
2964   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2965   PetscCall(PetscCalloc1(depth_, &sections_));
2966   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2967   for (d = depth_ - 1; d >= 0; d--) {
2968     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2969     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2970     for (i = 0; i < n; i++) {
2971       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2972       if (arr[i] >= start && arr[i] < end) {
2973         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2974         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2975       } else {
2976         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2977       }
2978     }
2979     PetscCall(PetscSectionSetUp(sections_[d]));
2980     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2981     PetscCall(PetscMalloc1(newn, &newarr));
2982     for (i = 0; i < n; i++) {
2983       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2984       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2985       if (cn > 1) {
2986         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2987         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2988       } else {
2989         newarr[co] = arr[i];
2990       }
2991     }
2992     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2993     arr = newarr;
2994     n   = newn;
2995   }
2996   PetscCall(ISRestoreIndices(points, &arr0));
2997   *depth = depth_;
2998   if (expandedPoints) *expandedPoints = expandedPoints_;
2999   else {
3000     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3001     PetscCall(PetscFree(expandedPoints_));
3002   }
3003   if (sections) *sections = sections_;
3004   else {
3005     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3006     PetscCall(PetscFree(sections_));
3007   }
3008   PetscFunctionReturn(0);
3009 }
3010 
3011 /*@
3012   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3013 
3014   Not Collective
3015 
3016   Input Parameters:
3017 + dm - The `DMPLEX`
3018 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3019 
3020   Output Parameters:
3021 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3022 . expandedPoints - (optional) An array of recursively expanded cones
3023 - sections - (optional) An array of sections which describe mappings from points to their cone points
3024 
3025   Level: advanced
3026 
3027   Note:
3028   See `DMPlexGetConeRecursive()`
3029 
3030 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3031           `DMPlexGetDepth()`, `IS`, `PetscSection`
3032 @*/
3033 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3034 {
3035   PetscInt d, depth_;
3036 
3037   PetscFunctionBegin;
3038   PetscCall(DMPlexGetDepth(dm, &depth_));
3039   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3040   if (depth) *depth = 0;
3041   if (expandedPoints) {
3042     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3043     PetscCall(PetscFree(*expandedPoints));
3044   }
3045   if (sections) {
3046     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3047     PetscCall(PetscFree(*sections));
3048   }
3049   PetscFunctionReturn(0);
3050 }
3051 
3052 /*@
3053   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
3054 
3055   Not Collective
3056 
3057   Input Parameters:
3058 + mesh - The `DMPLEX`
3059 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3060 - cone - An array of points which are on the in-edges for point p
3061 
3062   Level: beginner
3063 
3064   Note:
3065   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3066 
3067 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3068 @*/
3069 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3070 {
3071   DM_Plex *mesh = (DM_Plex *)dm->data;
3072   PetscInt pStart, pEnd;
3073   PetscInt dof, off, c;
3074 
3075   PetscFunctionBegin;
3076   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3077   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3078   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3079   if (dof) PetscValidIntPointer(cone, 3);
3080   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3081   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);
3082   for (c = 0; c < dof; ++c) {
3083     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);
3084     mesh->cones[off + c] = cone[c];
3085   }
3086   PetscFunctionReturn(0);
3087 }
3088 
3089 /*@C
3090   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3091 
3092   Not Collective
3093 
3094   Input Parameters:
3095 + mesh - The `DMPLEX`
3096 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3097 
3098   Output Parameter:
3099 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3100                     integer giving the prescription for cone traversal.
3101 
3102   Level: beginner
3103 
3104   Note:
3105   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3106   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3107   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3108   with the identity.
3109 
3110   Fortran Note:
3111   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3112   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3113 
3114 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3115 @*/
3116 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3117 {
3118   DM_Plex *mesh = (DM_Plex *)dm->data;
3119   PetscInt off;
3120 
3121   PetscFunctionBegin;
3122   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3123   if (PetscDefined(USE_DEBUG)) {
3124     PetscInt dof;
3125     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3126     if (dof) PetscValidPointer(coneOrientation, 3);
3127   }
3128   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3129 
3130   *coneOrientation = &mesh->coneOrientations[off];
3131   PetscFunctionReturn(0);
3132 }
3133 
3134 /*@
3135   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3136 
3137   Not Collective
3138 
3139   Input Parameters:
3140 + mesh - The `DMPLEX`
3141 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3142 - coneOrientation - An array of orientations
3143 
3144   Level: beginner
3145 
3146   Notes:
3147   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3148 
3149   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3150 
3151 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3152 @*/
3153 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3154 {
3155   DM_Plex *mesh = (DM_Plex *)dm->data;
3156   PetscInt pStart, pEnd;
3157   PetscInt dof, off, c;
3158 
3159   PetscFunctionBegin;
3160   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3161   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3162   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3163   if (dof) PetscValidIntPointer(coneOrientation, 3);
3164   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3165   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);
3166   for (c = 0; c < dof; ++c) {
3167     PetscInt cdof, o = coneOrientation[c];
3168 
3169     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3170     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);
3171     mesh->coneOrientations[off + c] = o;
3172   }
3173   PetscFunctionReturn(0);
3174 }
3175 
3176 /*@
3177   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3178 
3179   Not Collective
3180 
3181   Input Parameters:
3182 + mesh - The `DMPLEX`
3183 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3184 . conePos - The local index in the cone where the point should be put
3185 - conePoint - The mesh point to insert
3186 
3187   Level: beginner
3188 
3189 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3190 @*/
3191 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3192 {
3193   DM_Plex *mesh = (DM_Plex *)dm->data;
3194   PetscInt pStart, pEnd;
3195   PetscInt dof, off;
3196 
3197   PetscFunctionBegin;
3198   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3199   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3200   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);
3201   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);
3202   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3203   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3204   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);
3205   mesh->cones[off + conePos] = conePoint;
3206   PetscFunctionReturn(0);
3207 }
3208 
3209 /*@
3210   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3211 
3212   Not Collective
3213 
3214   Input Parameters:
3215 + mesh - The `DMPLEX`
3216 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3217 . conePos - The local index in the cone where the point should be put
3218 - coneOrientation - The point orientation to insert
3219 
3220   Level: beginner
3221 
3222   Note:
3223   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3224 
3225 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3226 @*/
3227 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3228 {
3229   DM_Plex *mesh = (DM_Plex *)dm->data;
3230   PetscInt pStart, pEnd;
3231   PetscInt dof, off;
3232 
3233   PetscFunctionBegin;
3234   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3235   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3236   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);
3237   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3238   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3239   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);
3240   mesh->coneOrientations[off + conePos] = coneOrientation;
3241   PetscFunctionReturn(0);
3242 }
3243 
3244 /*@
3245   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3246 
3247   Not Collective
3248 
3249   Input Parameters:
3250 + mesh - The `DMPLEX`
3251 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3252 
3253   Output Parameter:
3254 . size - The support size for point p
3255 
3256   Level: beginner
3257 
3258 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3259 @*/
3260 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3261 {
3262   DM_Plex *mesh = (DM_Plex *)dm->data;
3263 
3264   PetscFunctionBegin;
3265   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3266   PetscValidIntPointer(size, 3);
3267   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3268   PetscFunctionReturn(0);
3269 }
3270 
3271 /*@
3272   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3273 
3274   Not Collective
3275 
3276   Input Parameters:
3277 + mesh - The `DMPLEX`
3278 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3279 - size - The support size for point p
3280 
3281   Level: beginner
3282 
3283   Note:
3284   This should be called after DMPlexSetChart().
3285 
3286 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3287 @*/
3288 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3289 {
3290   DM_Plex *mesh = (DM_Plex *)dm->data;
3291 
3292   PetscFunctionBegin;
3293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3294   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3295   PetscFunctionReturn(0);
3296 }
3297 
3298 /*@C
3299   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3300 
3301   Not Collective
3302 
3303   Input Parameters:
3304 + mesh - The `DMPLEX`
3305 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3306 
3307   Output Parameter:
3308 . support - An array of points which are on the out-edges for point p
3309 
3310   Level: beginner
3311 
3312   Fortran Note:
3313   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3314   `DMPlexRestoreSupport()` is not needed/available in C.
3315 
3316 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3317 @*/
3318 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3319 {
3320   DM_Plex *mesh = (DM_Plex *)dm->data;
3321   PetscInt off;
3322 
3323   PetscFunctionBegin;
3324   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3325   PetscValidPointer(support, 3);
3326   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3327   *support = &mesh->supports[off];
3328   PetscFunctionReturn(0);
3329 }
3330 
3331 /*@
3332   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3333 
3334   Not Collective
3335 
3336   Input Parameters:
3337 + mesh - The `DMPLEX`
3338 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3339 - support - An array of points which are on the out-edges for point p
3340 
3341   Level: beginner
3342 
3343   Note:
3344   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3345 
3346 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3347 @*/
3348 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3349 {
3350   DM_Plex *mesh = (DM_Plex *)dm->data;
3351   PetscInt pStart, pEnd;
3352   PetscInt dof, off, c;
3353 
3354   PetscFunctionBegin;
3355   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3356   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3357   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3358   if (dof) PetscValidIntPointer(support, 3);
3359   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3360   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);
3361   for (c = 0; c < dof; ++c) {
3362     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);
3363     mesh->supports[off + c] = support[c];
3364   }
3365   PetscFunctionReturn(0);
3366 }
3367 
3368 /*@
3369   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3370 
3371   Not Collective
3372 
3373   Input Parameters:
3374 + mesh - The `DMPLEX`
3375 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3376 . supportPos - The local index in the cone where the point should be put
3377 - supportPoint - The mesh point to insert
3378 
3379   Level: beginner
3380 
3381 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3382 @*/
3383 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3384 {
3385   DM_Plex *mesh = (DM_Plex *)dm->data;
3386   PetscInt pStart, pEnd;
3387   PetscInt dof, off;
3388 
3389   PetscFunctionBegin;
3390   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3391   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3392   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3393   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3394   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);
3395   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);
3396   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);
3397   mesh->supports[off + supportPos] = supportPoint;
3398   PetscFunctionReturn(0);
3399 }
3400 
3401 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3402 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3403 {
3404   switch (ct) {
3405   case DM_POLYTOPE_SEGMENT:
3406     if (o == -1) return -2;
3407     break;
3408   case DM_POLYTOPE_TRIANGLE:
3409     if (o == -3) return -1;
3410     if (o == -2) return -3;
3411     if (o == -1) return -2;
3412     break;
3413   case DM_POLYTOPE_QUADRILATERAL:
3414     if (o == -4) return -2;
3415     if (o == -3) return -1;
3416     if (o == -2) return -4;
3417     if (o == -1) return -3;
3418     break;
3419   default:
3420     return o;
3421   }
3422   return o;
3423 }
3424 
3425 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3426 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3427 {
3428   switch (ct) {
3429   case DM_POLYTOPE_SEGMENT:
3430     if ((o == -2) || (o == 1)) return -1;
3431     if (o == -1) return 0;
3432     break;
3433   case DM_POLYTOPE_TRIANGLE:
3434     if (o == -3) return -2;
3435     if (o == -2) return -1;
3436     if (o == -1) return -3;
3437     break;
3438   case DM_POLYTOPE_QUADRILATERAL:
3439     if (o == -4) return -2;
3440     if (o == -3) return -1;
3441     if (o == -2) return -4;
3442     if (o == -1) return -3;
3443     break;
3444   default:
3445     return o;
3446   }
3447   return o;
3448 }
3449 
3450 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3451 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3452 {
3453   PetscInt pStart, pEnd, p;
3454 
3455   PetscFunctionBegin;
3456   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3457   for (p = pStart; p < pEnd; ++p) {
3458     const PetscInt *cone, *ornt;
3459     PetscInt        coneSize, c;
3460 
3461     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3462     PetscCall(DMPlexGetCone(dm, p, &cone));
3463     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3464     for (c = 0; c < coneSize; ++c) {
3465       DMPolytopeType ct;
3466       const PetscInt o = ornt[c];
3467 
3468       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3469       switch (ct) {
3470       case DM_POLYTOPE_SEGMENT:
3471         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3472         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3473         break;
3474       case DM_POLYTOPE_TRIANGLE:
3475         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3476         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3477         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3478         break;
3479       case DM_POLYTOPE_QUADRILATERAL:
3480         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3481         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3482         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3483         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3484         break;
3485       default:
3486         break;
3487       }
3488     }
3489   }
3490   PetscFunctionReturn(0);
3491 }
3492 
3493 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3494 {
3495   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3496   PetscInt       *closure;
3497   const PetscInt *tmp = NULL, *tmpO = NULL;
3498   PetscInt        off = 0, tmpSize, t;
3499 
3500   PetscFunctionBeginHot;
3501   if (ornt) {
3502     PetscCall(DMPlexGetCellType(dm, p, &ct));
3503     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3504   }
3505   if (*points) {
3506     closure = *points;
3507   } else {
3508     PetscInt maxConeSize, maxSupportSize;
3509     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3510     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3511   }
3512   if (useCone) {
3513     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3514     PetscCall(DMPlexGetCone(dm, p, &tmp));
3515     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3516   } else {
3517     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3518     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3519   }
3520   if (ct == DM_POLYTOPE_UNKNOWN) {
3521     closure[off++] = p;
3522     closure[off++] = 0;
3523     for (t = 0; t < tmpSize; ++t) {
3524       closure[off++] = tmp[t];
3525       closure[off++] = tmpO ? tmpO[t] : 0;
3526     }
3527   } else {
3528     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3529 
3530     /* We assume that cells with a valid type have faces with a valid type */
3531     closure[off++] = p;
3532     closure[off++] = ornt;
3533     for (t = 0; t < tmpSize; ++t) {
3534       DMPolytopeType ft;
3535 
3536       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3537       closure[off++] = tmp[arr[t]];
3538       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3539     }
3540   }
3541   if (numPoints) *numPoints = tmpSize + 1;
3542   if (points) *points = closure;
3543   PetscFunctionReturn(0);
3544 }
3545 
3546 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3547 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3548 {
3549   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3550   const PetscInt *cone, *ornt;
3551   PetscInt       *pts, *closure = NULL;
3552   DMPolytopeType  ft;
3553   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3554   PetscInt        dim, coneSize, c, d, clSize, cl;
3555 
3556   PetscFunctionBeginHot;
3557   PetscCall(DMGetDimension(dm, &dim));
3558   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3559   PetscCall(DMPlexGetCone(dm, point, &cone));
3560   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3561   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3562   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3563   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3564   maxSize       = PetscMax(coneSeries, supportSeries);
3565   if (*points) {
3566     pts = *points;
3567   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3568   c        = 0;
3569   pts[c++] = point;
3570   pts[c++] = o;
3571   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3572   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3573   for (cl = 0; cl < clSize * 2; cl += 2) {
3574     pts[c++] = closure[cl];
3575     pts[c++] = closure[cl + 1];
3576   }
3577   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3578   for (cl = 0; cl < clSize * 2; cl += 2) {
3579     pts[c++] = closure[cl];
3580     pts[c++] = closure[cl + 1];
3581   }
3582   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3583   for (d = 2; d < coneSize; ++d) {
3584     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3585     pts[c++] = cone[arr[d * 2 + 0]];
3586     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3587   }
3588   if (dim >= 3) {
3589     for (d = 2; d < coneSize; ++d) {
3590       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3591       const PetscInt *fcone, *fornt;
3592       PetscInt        fconeSize, fc, i;
3593 
3594       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3595       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3596       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3597       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3598       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3599       for (fc = 0; fc < fconeSize; ++fc) {
3600         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3601         const PetscInt co = farr[fc * 2 + 1];
3602 
3603         for (i = 0; i < c; i += 2)
3604           if (pts[i] == cp) break;
3605         if (i == c) {
3606           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3607           pts[c++] = cp;
3608           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3609         }
3610       }
3611     }
3612   }
3613   *numPoints = c / 2;
3614   *points    = pts;
3615   PetscFunctionReturn(0);
3616 }
3617 
3618 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3619 {
3620   DMPolytopeType ct;
3621   PetscInt      *closure, *fifo;
3622   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3623   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3624   PetscInt       depth, maxSize;
3625 
3626   PetscFunctionBeginHot;
3627   PetscCall(DMPlexGetDepth(dm, &depth));
3628   if (depth == 1) {
3629     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3630     PetscFunctionReturn(0);
3631   }
3632   PetscCall(DMPlexGetCellType(dm, p, &ct));
3633   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3634   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3635     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3636     PetscFunctionReturn(0);
3637   }
3638   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3639   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3640   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3641   maxSize       = PetscMax(coneSeries, supportSeries);
3642   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3643   if (*points) {
3644     closure = *points;
3645   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3646   closure[closureSize++] = p;
3647   closure[closureSize++] = ornt;
3648   fifo[fifoSize++]       = p;
3649   fifo[fifoSize++]       = ornt;
3650   fifo[fifoSize++]       = ct;
3651   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3652   while (fifoSize - fifoStart) {
3653     const PetscInt       q    = fifo[fifoStart++];
3654     const PetscInt       o    = fifo[fifoStart++];
3655     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3656     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3657     const PetscInt      *tmp, *tmpO;
3658     PetscInt             tmpSize, t;
3659 
3660     if (PetscDefined(USE_DEBUG)) {
3661       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3662       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);
3663     }
3664     if (useCone) {
3665       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3666       PetscCall(DMPlexGetCone(dm, q, &tmp));
3667       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3668     } else {
3669       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3670       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3671       tmpO = NULL;
3672     }
3673     for (t = 0; t < tmpSize; ++t) {
3674       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3675       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3676       const PetscInt cp = tmp[ip];
3677       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3678       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3679       PetscInt       c;
3680 
3681       /* Check for duplicate */
3682       for (c = 0; c < closureSize; c += 2) {
3683         if (closure[c] == cp) break;
3684       }
3685       if (c == closureSize) {
3686         closure[closureSize++] = cp;
3687         closure[closureSize++] = co;
3688         fifo[fifoSize++]       = cp;
3689         fifo[fifoSize++]       = co;
3690         fifo[fifoSize++]       = ct;
3691       }
3692     }
3693   }
3694   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3695   if (numPoints) *numPoints = closureSize / 2;
3696   if (points) *points = closure;
3697   PetscFunctionReturn(0);
3698 }
3699 
3700 /*@C
3701   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3702 
3703   Not Collective
3704 
3705   Input Parameters:
3706 + dm      - The `DMPLEX`
3707 . p       - The mesh point
3708 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3709 
3710   Input/Output Parameter:
3711 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3712            if NULL on input, internal storage will be returned, otherwise the provided array is used
3713 
3714   Output Parameter:
3715 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3716 
3717   Level: beginner
3718 
3719   Note:
3720   If using internal storage (points is NULL on input), each call overwrites the last output.
3721 
3722   Fortran Note:
3723   The numPoints argument is not present in the Fortran binding since it is internal to the array.
3724 
3725 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3726 @*/
3727 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3728 {
3729   PetscFunctionBeginHot;
3730   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3731   if (numPoints) PetscValidIntPointer(numPoints, 4);
3732   if (points) PetscValidPointer(points, 5);
3733   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3734   PetscFunctionReturn(0);
3735 }
3736 
3737 /*@C
3738   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3739 
3740   Not Collective
3741 
3742   Input Parameters:
3743 + dm        - The `DMPLEX`
3744 . p         - The mesh point
3745 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3746 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3747 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3748 
3749   Level: beginner
3750 
3751   Note:
3752   If not using internal storage (points is not NULL on input), this call is unnecessary
3753 
3754 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3755 @*/
3756 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3757 {
3758   PetscFunctionBeginHot;
3759   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3760   if (numPoints) *numPoints = 0;
3761   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3762   PetscFunctionReturn(0);
3763 }
3764 
3765 /*@
3766   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3767 
3768   Not Collective
3769 
3770   Input Parameter:
3771 . mesh - The `DMPLEX`
3772 
3773   Output Parameters:
3774 + maxConeSize - The maximum number of in-edges
3775 - maxSupportSize - The maximum number of out-edges
3776 
3777   Level: beginner
3778 
3779 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3780 @*/
3781 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3782 {
3783   DM_Plex *mesh = (DM_Plex *)dm->data;
3784 
3785   PetscFunctionBegin;
3786   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3787   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3788   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3789   PetscFunctionReturn(0);
3790 }
3791 
3792 PetscErrorCode DMSetUp_Plex(DM dm)
3793 {
3794   DM_Plex *mesh = (DM_Plex *)dm->data;
3795   PetscInt size, maxSupportSize;
3796 
3797   PetscFunctionBegin;
3798   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3799   PetscCall(PetscSectionSetUp(mesh->coneSection));
3800   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3801   PetscCall(PetscMalloc1(size, &mesh->cones));
3802   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3803   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3804   if (maxSupportSize) {
3805     PetscCall(PetscSectionSetUp(mesh->supportSection));
3806     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3807     PetscCall(PetscMalloc1(size, &mesh->supports));
3808   }
3809   PetscFunctionReturn(0);
3810 }
3811 
3812 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3813 {
3814   PetscFunctionBegin;
3815   if (subdm) PetscCall(DMClone(dm, subdm));
3816   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3817   if (subdm) (*subdm)->useNatural = dm->useNatural;
3818   if (dm->useNatural && dm->sfMigration) {
3819     PetscSF sfNatural;
3820 
3821     (*subdm)->sfMigration = dm->sfMigration;
3822     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3823     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3824     (*subdm)->sfNatural = sfNatural;
3825   }
3826   PetscFunctionReturn(0);
3827 }
3828 
3829 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3830 {
3831   PetscInt i = 0;
3832 
3833   PetscFunctionBegin;
3834   PetscCall(DMClone(dms[0], superdm));
3835   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3836   (*superdm)->useNatural = PETSC_FALSE;
3837   for (i = 0; i < len; i++) {
3838     if (dms[i]->useNatural && dms[i]->sfMigration) {
3839       PetscSF sfNatural;
3840 
3841       (*superdm)->sfMigration = dms[i]->sfMigration;
3842       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3843       (*superdm)->useNatural = PETSC_TRUE;
3844       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3845       (*superdm)->sfNatural = sfNatural;
3846       break;
3847     }
3848   }
3849   PetscFunctionReturn(0);
3850 }
3851 
3852 /*@
3853   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3854 
3855   Not Collective
3856 
3857   Input Parameter:
3858 . mesh - The `DMPLEX`
3859 
3860   Level: beginner
3861 
3862   Note:
3863   This should be called after all calls to `DMPlexSetCone()`
3864 
3865 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3866 @*/
3867 PetscErrorCode DMPlexSymmetrize(DM dm)
3868 {
3869   DM_Plex  *mesh = (DM_Plex *)dm->data;
3870   PetscInt *offsets;
3871   PetscInt  supportSize;
3872   PetscInt  pStart, pEnd, p;
3873 
3874   PetscFunctionBegin;
3875   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3876   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3877   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3878   /* Calculate support sizes */
3879   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3880   for (p = pStart; p < pEnd; ++p) {
3881     PetscInt dof, off, c;
3882 
3883     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3884     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3885     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3886   }
3887   PetscCall(PetscSectionSetUp(mesh->supportSection));
3888   /* Calculate supports */
3889   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3890   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3891   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3892   for (p = pStart; p < pEnd; ++p) {
3893     PetscInt dof, off, c;
3894 
3895     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3896     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3897     for (c = off; c < off + dof; ++c) {
3898       const PetscInt q = mesh->cones[c];
3899       PetscInt       offS;
3900 
3901       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3902 
3903       mesh->supports[offS + offsets[q]] = p;
3904       ++offsets[q];
3905     }
3906   }
3907   PetscCall(PetscFree(offsets));
3908   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
3909   PetscFunctionReturn(0);
3910 }
3911 
3912 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3913 {
3914   IS stratumIS;
3915 
3916   PetscFunctionBegin;
3917   if (pStart >= pEnd) PetscFunctionReturn(0);
3918   if (PetscDefined(USE_DEBUG)) {
3919     PetscInt  qStart, qEnd, numLevels, level;
3920     PetscBool overlap = PETSC_FALSE;
3921     PetscCall(DMLabelGetNumValues(label, &numLevels));
3922     for (level = 0; level < numLevels; level++) {
3923       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3924       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
3925         overlap = PETSC_TRUE;
3926         break;
3927       }
3928     }
3929     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);
3930   }
3931   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
3932   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3933   PetscCall(ISDestroy(&stratumIS));
3934   PetscFunctionReturn(0);
3935 }
3936 
3937 /*@
3938   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3939   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3940   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3941   the DAG.
3942 
3943   Collective on dm
3944 
3945   Input Parameter:
3946 . mesh - The `DMPLEX`
3947 
3948   Level: beginner
3949 
3950   Notes:
3951   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3952   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3953   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
3954   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3955   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
3956 
3957   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3958   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3959   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
3960   to interpolate only that one (e0), so that
3961 .vb
3962   cone(c0) = {e0, v2}
3963   cone(e0) = {v0, v1}
3964 .ve
3965   If `DMPlexStratify()` is run on this mesh, it will give depths
3966 .vb
3967    depth 0 = {v0, v1, v2}
3968    depth 1 = {e0, c0}
3969 .ve
3970   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3971 
3972   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
3973 
3974 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3975 @*/
3976 PetscErrorCode DMPlexStratify(DM dm)
3977 {
3978   DM_Plex *mesh = (DM_Plex *)dm->data;
3979   DMLabel  label;
3980   PetscInt pStart, pEnd, p;
3981   PetscInt numRoots = 0, numLeaves = 0;
3982 
3983   PetscFunctionBegin;
3984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3985   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
3986 
3987   /* Create depth label */
3988   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3989   PetscCall(DMCreateLabel(dm, "depth"));
3990   PetscCall(DMPlexGetDepthLabel(dm, &label));
3991 
3992   {
3993     /* Initialize roots and count leaves */
3994     PetscInt sMin = PETSC_MAX_INT;
3995     PetscInt sMax = PETSC_MIN_INT;
3996     PetscInt coneSize, supportSize;
3997 
3998     for (p = pStart; p < pEnd; ++p) {
3999       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4000       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4001       if (!coneSize && supportSize) {
4002         sMin = PetscMin(p, sMin);
4003         sMax = PetscMax(p, sMax);
4004         ++numRoots;
4005       } else if (!supportSize && coneSize) {
4006         ++numLeaves;
4007       } else if (!supportSize && !coneSize) {
4008         /* Isolated points */
4009         sMin = PetscMin(p, sMin);
4010         sMax = PetscMax(p, sMax);
4011       }
4012     }
4013     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4014   }
4015 
4016   if (numRoots + numLeaves == (pEnd - pStart)) {
4017     PetscInt sMin = PETSC_MAX_INT;
4018     PetscInt sMax = PETSC_MIN_INT;
4019     PetscInt coneSize, supportSize;
4020 
4021     for (p = pStart; p < pEnd; ++p) {
4022       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4023       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4024       if (!supportSize && coneSize) {
4025         sMin = PetscMin(p, sMin);
4026         sMax = PetscMax(p, sMax);
4027       }
4028     }
4029     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4030   } else {
4031     PetscInt level = 0;
4032     PetscInt qStart, qEnd, q;
4033 
4034     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4035     while (qEnd > qStart) {
4036       PetscInt sMin = PETSC_MAX_INT;
4037       PetscInt sMax = PETSC_MIN_INT;
4038 
4039       for (q = qStart; q < qEnd; ++q) {
4040         const PetscInt *support;
4041         PetscInt        supportSize, s;
4042 
4043         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4044         PetscCall(DMPlexGetSupport(dm, q, &support));
4045         for (s = 0; s < supportSize; ++s) {
4046           sMin = PetscMin(support[s], sMin);
4047           sMax = PetscMax(support[s], sMax);
4048         }
4049       }
4050       PetscCall(DMLabelGetNumValues(label, &level));
4051       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4052       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4053     }
4054   }
4055   { /* just in case there is an empty process */
4056     PetscInt numValues, maxValues = 0, v;
4057 
4058     PetscCall(DMLabelGetNumValues(label, &numValues));
4059     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4060     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4061   }
4062   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4063   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4064   PetscFunctionReturn(0);
4065 }
4066 
4067 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4068 {
4069   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4070   PetscInt       dim, depth, pheight, coneSize;
4071 
4072   PetscFunctionBeginHot;
4073   PetscCall(DMGetDimension(dm, &dim));
4074   PetscCall(DMPlexGetDepth(dm, &depth));
4075   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4076   pheight = depth - pdepth;
4077   if (depth <= 1) {
4078     switch (pdepth) {
4079     case 0:
4080       ct = DM_POLYTOPE_POINT;
4081       break;
4082     case 1:
4083       switch (coneSize) {
4084       case 2:
4085         ct = DM_POLYTOPE_SEGMENT;
4086         break;
4087       case 3:
4088         ct = DM_POLYTOPE_TRIANGLE;
4089         break;
4090       case 4:
4091         switch (dim) {
4092         case 2:
4093           ct = DM_POLYTOPE_QUADRILATERAL;
4094           break;
4095         case 3:
4096           ct = DM_POLYTOPE_TETRAHEDRON;
4097           break;
4098         default:
4099           break;
4100         }
4101         break;
4102       case 5:
4103         ct = DM_POLYTOPE_PYRAMID;
4104         break;
4105       case 6:
4106         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4107         break;
4108       case 8:
4109         ct = DM_POLYTOPE_HEXAHEDRON;
4110         break;
4111       default:
4112         break;
4113       }
4114     }
4115   } else {
4116     if (pdepth == 0) {
4117       ct = DM_POLYTOPE_POINT;
4118     } else if (pheight == 0) {
4119       switch (dim) {
4120       case 1:
4121         switch (coneSize) {
4122         case 2:
4123           ct = DM_POLYTOPE_SEGMENT;
4124           break;
4125         default:
4126           break;
4127         }
4128         break;
4129       case 2:
4130         switch (coneSize) {
4131         case 3:
4132           ct = DM_POLYTOPE_TRIANGLE;
4133           break;
4134         case 4:
4135           ct = DM_POLYTOPE_QUADRILATERAL;
4136           break;
4137         default:
4138           break;
4139         }
4140         break;
4141       case 3:
4142         switch (coneSize) {
4143         case 4:
4144           ct = DM_POLYTOPE_TETRAHEDRON;
4145           break;
4146         case 5: {
4147           const PetscInt *cone;
4148           PetscInt        faceConeSize;
4149 
4150           PetscCall(DMPlexGetCone(dm, p, &cone));
4151           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4152           switch (faceConeSize) {
4153           case 3:
4154             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4155             break;
4156           case 4:
4157             ct = DM_POLYTOPE_PYRAMID;
4158             break;
4159           }
4160         } break;
4161         case 6:
4162           ct = DM_POLYTOPE_HEXAHEDRON;
4163           break;
4164         default:
4165           break;
4166         }
4167         break;
4168       default:
4169         break;
4170       }
4171     } else if (pheight > 0) {
4172       switch (coneSize) {
4173       case 2:
4174         ct = DM_POLYTOPE_SEGMENT;
4175         break;
4176       case 3:
4177         ct = DM_POLYTOPE_TRIANGLE;
4178         break;
4179       case 4:
4180         ct = DM_POLYTOPE_QUADRILATERAL;
4181         break;
4182       default:
4183         break;
4184       }
4185     }
4186   }
4187   *pt = ct;
4188   PetscFunctionReturn(0);
4189 }
4190 
4191 /*@
4192   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4193 
4194   Collective on dm
4195 
4196   Input Parameter:
4197 . mesh - The `DMPLEX`
4198 
4199   Level: developer
4200 
4201   Note:
4202   This function is normally called automatically when a cell type is requested. It creates an
4203   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4204   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4205 
4206   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4207 
4208 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4209 @*/
4210 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4211 {
4212   DM_Plex *mesh;
4213   DMLabel  ctLabel;
4214   PetscInt pStart, pEnd, p;
4215 
4216   PetscFunctionBegin;
4217   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4218   mesh = (DM_Plex *)dm->data;
4219   PetscCall(DMCreateLabel(dm, "celltype"));
4220   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4221   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4222   for (p = pStart; p < pEnd; ++p) {
4223     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4224     PetscInt       pdepth;
4225 
4226     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4227     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4228     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4229     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4230   }
4231   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4232   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4233   PetscFunctionReturn(0);
4234 }
4235 
4236 /*@C
4237   DMPlexGetJoin - Get an array for the join of the set of points
4238 
4239   Not Collective
4240 
4241   Input Parameters:
4242 + dm - The `DMPLEX` object
4243 . numPoints - The number of input points for the join
4244 - points - The input points
4245 
4246   Output Parameters:
4247 + numCoveredPoints - The number of points in the join
4248 - coveredPoints - The points in the join
4249 
4250   Level: intermediate
4251 
4252   Note:
4253   Currently, this is restricted to a single level join
4254 
4255   Fortran Note:
4256   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4257 
4258 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4259 @*/
4260 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4261 {
4262   DM_Plex  *mesh = (DM_Plex *)dm->data;
4263   PetscInt *join[2];
4264   PetscInt  joinSize, i = 0;
4265   PetscInt  dof, off, p, c, m;
4266   PetscInt  maxSupportSize;
4267 
4268   PetscFunctionBegin;
4269   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4270   PetscValidIntPointer(points, 3);
4271   PetscValidIntPointer(numCoveredPoints, 4);
4272   PetscValidPointer(coveredPoints, 5);
4273   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4274   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4275   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4276   /* Copy in support of first point */
4277   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4278   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4279   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4280   /* Check each successive support */
4281   for (p = 1; p < numPoints; ++p) {
4282     PetscInt newJoinSize = 0;
4283 
4284     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4285     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4286     for (c = 0; c < dof; ++c) {
4287       const PetscInt point = mesh->supports[off + c];
4288 
4289       for (m = 0; m < joinSize; ++m) {
4290         if (point == join[i][m]) {
4291           join[1 - i][newJoinSize++] = point;
4292           break;
4293         }
4294       }
4295     }
4296     joinSize = newJoinSize;
4297     i        = 1 - i;
4298   }
4299   *numCoveredPoints = joinSize;
4300   *coveredPoints    = join[i];
4301   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4302   PetscFunctionReturn(0);
4303 }
4304 
4305 /*@C
4306   DMPlexRestoreJoin - Restore an array for the join of the set of points
4307 
4308   Not Collective
4309 
4310   Input Parameters:
4311 + dm - The `DMPLEX` object
4312 . numPoints - The number of input points for the join
4313 - points - The input points
4314 
4315   Output Parameters:
4316 + numCoveredPoints - The number of points in the join
4317 - coveredPoints - The points in the join
4318 
4319   Level: intermediate
4320 
4321   Fortran Note:
4322   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4323 
4324 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4325 @*/
4326 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4327 {
4328   PetscFunctionBegin;
4329   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4330   if (points) PetscValidIntPointer(points, 3);
4331   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4332   PetscValidPointer(coveredPoints, 5);
4333   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4334   if (numCoveredPoints) *numCoveredPoints = 0;
4335   PetscFunctionReturn(0);
4336 }
4337 
4338 /*@C
4339   DMPlexGetFullJoin - Get an array for the join of the set of points
4340 
4341   Not Collective
4342 
4343   Input Parameters:
4344 + dm - The `DMPLEX` object
4345 . numPoints - The number of input points for the join
4346 - points - The input points
4347 
4348   Output Parameters:
4349 + numCoveredPoints - The number of points in the join
4350 - coveredPoints - The points in the join
4351 
4352   Level: intermediate
4353 
4354   Fortran Note:
4355   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4356 
4357 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4358 @*/
4359 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4360 {
4361   PetscInt *offsets, **closures;
4362   PetscInt *join[2];
4363   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4364   PetscInt  p, d, c, m, ms;
4365 
4366   PetscFunctionBegin;
4367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4368   PetscValidIntPointer(points, 3);
4369   PetscValidIntPointer(numCoveredPoints, 4);
4370   PetscValidPointer(coveredPoints, 5);
4371 
4372   PetscCall(DMPlexGetDepth(dm, &depth));
4373   PetscCall(PetscCalloc1(numPoints, &closures));
4374   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4375   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4376   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4377   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4378   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4379 
4380   for (p = 0; p < numPoints; ++p) {
4381     PetscInt closureSize;
4382 
4383     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4384 
4385     offsets[p * (depth + 2) + 0] = 0;
4386     for (d = 0; d < depth + 1; ++d) {
4387       PetscInt pStart, pEnd, i;
4388 
4389       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4390       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4391         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4392           offsets[p * (depth + 2) + d + 1] = i;
4393           break;
4394         }
4395       }
4396       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4397     }
4398     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);
4399   }
4400   for (d = 0; d < depth + 1; ++d) {
4401     PetscInt dof;
4402 
4403     /* Copy in support of first point */
4404     dof = offsets[d + 1] - offsets[d];
4405     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4406     /* Check each successive cone */
4407     for (p = 1; p < numPoints && joinSize; ++p) {
4408       PetscInt newJoinSize = 0;
4409 
4410       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4411       for (c = 0; c < dof; ++c) {
4412         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4413 
4414         for (m = 0; m < joinSize; ++m) {
4415           if (point == join[i][m]) {
4416             join[1 - i][newJoinSize++] = point;
4417             break;
4418           }
4419         }
4420       }
4421       joinSize = newJoinSize;
4422       i        = 1 - i;
4423     }
4424     if (joinSize) break;
4425   }
4426   *numCoveredPoints = joinSize;
4427   *coveredPoints    = join[i];
4428   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4429   PetscCall(PetscFree(closures));
4430   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4431   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4432   PetscFunctionReturn(0);
4433 }
4434 
4435 /*@C
4436   DMPlexGetMeet - Get an array for the meet of the set of points
4437 
4438   Not Collective
4439 
4440   Input Parameters:
4441 + dm - The `DMPLEX` object
4442 . numPoints - The number of input points for the meet
4443 - points - The input points
4444 
4445   Output Parameters:
4446 + numCoveredPoints - The number of points in the meet
4447 - coveredPoints - The points in the meet
4448 
4449   Level: intermediate
4450 
4451   Note:
4452   Currently, this is restricted to a single level meet
4453 
4454   Fortran Notes:
4455   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4456 
4457 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4458 @*/
4459 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4460 {
4461   DM_Plex  *mesh = (DM_Plex *)dm->data;
4462   PetscInt *meet[2];
4463   PetscInt  meetSize, i = 0;
4464   PetscInt  dof, off, p, c, m;
4465   PetscInt  maxConeSize;
4466 
4467   PetscFunctionBegin;
4468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4469   PetscValidIntPointer(points, 3);
4470   PetscValidIntPointer(numCoveringPoints, 4);
4471   PetscValidPointer(coveringPoints, 5);
4472   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4473   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4474   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4475   /* Copy in cone of first point */
4476   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4477   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4478   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4479   /* Check each successive cone */
4480   for (p = 1; p < numPoints; ++p) {
4481     PetscInt newMeetSize = 0;
4482 
4483     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4484     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4485     for (c = 0; c < dof; ++c) {
4486       const PetscInt point = mesh->cones[off + c];
4487 
4488       for (m = 0; m < meetSize; ++m) {
4489         if (point == meet[i][m]) {
4490           meet[1 - i][newMeetSize++] = point;
4491           break;
4492         }
4493       }
4494     }
4495     meetSize = newMeetSize;
4496     i        = 1 - i;
4497   }
4498   *numCoveringPoints = meetSize;
4499   *coveringPoints    = meet[i];
4500   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4501   PetscFunctionReturn(0);
4502 }
4503 
4504 /*@C
4505   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4506 
4507   Not Collective
4508 
4509   Input Parameters:
4510 + dm - The `DMPLEX` object
4511 . numPoints - The number of input points for the meet
4512 - points - The input points
4513 
4514   Output Parameters:
4515 + numCoveredPoints - The number of points in the meet
4516 - coveredPoints - The points in the meet
4517 
4518   Level: intermediate
4519 
4520   Fortran Note:
4521   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4522 
4523 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4524 @*/
4525 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4526 {
4527   PetscFunctionBegin;
4528   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4529   if (points) PetscValidIntPointer(points, 3);
4530   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4531   PetscValidPointer(coveredPoints, 5);
4532   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4533   if (numCoveredPoints) *numCoveredPoints = 0;
4534   PetscFunctionReturn(0);
4535 }
4536 
4537 /*@C
4538   DMPlexGetFullMeet - Get an array for the meet of the set of points
4539 
4540   Not Collective
4541 
4542   Input Parameters:
4543 + dm - The `DMPLEX` object
4544 . numPoints - The number of input points for the meet
4545 - points - The input points
4546 
4547   Output Parameters:
4548 + numCoveredPoints - The number of points in the meet
4549 - coveredPoints - The points in the meet
4550 
4551   Level: intermediate
4552 
4553   Fortran Note:
4554   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4555 
4556 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4557 @*/
4558 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4559 {
4560   PetscInt *offsets, **closures;
4561   PetscInt *meet[2];
4562   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4563   PetscInt  p, h, c, m, mc;
4564 
4565   PetscFunctionBegin;
4566   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4567   PetscValidIntPointer(points, 3);
4568   PetscValidIntPointer(numCoveredPoints, 4);
4569   PetscValidPointer(coveredPoints, 5);
4570 
4571   PetscCall(DMPlexGetDepth(dm, &height));
4572   PetscCall(PetscMalloc1(numPoints, &closures));
4573   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4574   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4575   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4576   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4577   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4578 
4579   for (p = 0; p < numPoints; ++p) {
4580     PetscInt closureSize;
4581 
4582     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4583 
4584     offsets[p * (height + 2) + 0] = 0;
4585     for (h = 0; h < height + 1; ++h) {
4586       PetscInt pStart, pEnd, i;
4587 
4588       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4589       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4590         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4591           offsets[p * (height + 2) + h + 1] = i;
4592           break;
4593         }
4594       }
4595       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4596     }
4597     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);
4598   }
4599   for (h = 0; h < height + 1; ++h) {
4600     PetscInt dof;
4601 
4602     /* Copy in cone of first point */
4603     dof = offsets[h + 1] - offsets[h];
4604     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4605     /* Check each successive cone */
4606     for (p = 1; p < numPoints && meetSize; ++p) {
4607       PetscInt newMeetSize = 0;
4608 
4609       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4610       for (c = 0; c < dof; ++c) {
4611         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4612 
4613         for (m = 0; m < meetSize; ++m) {
4614           if (point == meet[i][m]) {
4615             meet[1 - i][newMeetSize++] = point;
4616             break;
4617           }
4618         }
4619       }
4620       meetSize = newMeetSize;
4621       i        = 1 - i;
4622     }
4623     if (meetSize) break;
4624   }
4625   *numCoveredPoints = meetSize;
4626   *coveredPoints    = meet[i];
4627   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4628   PetscCall(PetscFree(closures));
4629   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4630   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4631   PetscFunctionReturn(0);
4632 }
4633 
4634 /*@C
4635   DMPlexEqual - Determine if two `DM` have the same topology
4636 
4637   Not Collective
4638 
4639   Input Parameters:
4640 + dmA - A `DMPLEX` object
4641 - dmB - A `DMPLEX` object
4642 
4643   Output Parameters:
4644 . equal - `PETSC_TRUE` if the topologies are identical
4645 
4646   Level: intermediate
4647 
4648   Note:
4649   We are not solving graph isomorphism, so we do not permute.
4650 
4651 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4652 @*/
4653 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4654 {
4655   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4656 
4657   PetscFunctionBegin;
4658   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4659   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4660   PetscValidBoolPointer(equal, 3);
4661 
4662   *equal = PETSC_FALSE;
4663   PetscCall(DMPlexGetDepth(dmA, &depth));
4664   PetscCall(DMPlexGetDepth(dmB, &depthB));
4665   if (depth != depthB) PetscFunctionReturn(0);
4666   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4667   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4668   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4669   for (p = pStart; p < pEnd; ++p) {
4670     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4671     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4672 
4673     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4674     PetscCall(DMPlexGetCone(dmA, p, &cone));
4675     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4676     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4677     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4678     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4679     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4680     for (c = 0; c < coneSize; ++c) {
4681       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4682       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4683     }
4684     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4685     PetscCall(DMPlexGetSupport(dmA, p, &support));
4686     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4687     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4688     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4689     for (s = 0; s < supportSize; ++s) {
4690       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4691     }
4692   }
4693   *equal = PETSC_TRUE;
4694   PetscFunctionReturn(0);
4695 }
4696 
4697 /*@C
4698   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4699 
4700   Not Collective
4701 
4702   Input Parameters:
4703 + dm         - The `DMPLEX`
4704 . cellDim    - The cell dimension
4705 - numCorners - The number of vertices on a cell
4706 
4707   Output Parameters:
4708 . numFaceVertices - The number of vertices on a face
4709 
4710   Level: developer
4711 
4712   Note:
4713   Of course this can only work for a restricted set of symmetric shapes
4714 
4715 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4716 @*/
4717 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4718 {
4719   MPI_Comm comm;
4720 
4721   PetscFunctionBegin;
4722   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4723   PetscValidIntPointer(numFaceVertices, 4);
4724   switch (cellDim) {
4725   case 0:
4726     *numFaceVertices = 0;
4727     break;
4728   case 1:
4729     *numFaceVertices = 1;
4730     break;
4731   case 2:
4732     switch (numCorners) {
4733     case 3:                 /* triangle */
4734       *numFaceVertices = 2; /* Edge has 2 vertices */
4735       break;
4736     case 4:                 /* quadrilateral */
4737       *numFaceVertices = 2; /* Edge has 2 vertices */
4738       break;
4739     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4740       *numFaceVertices = 3; /* Edge has 3 vertices */
4741       break;
4742     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4743       *numFaceVertices = 3; /* Edge has 3 vertices */
4744       break;
4745     default:
4746       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4747     }
4748     break;
4749   case 3:
4750     switch (numCorners) {
4751     case 4:                 /* tetradehdron */
4752       *numFaceVertices = 3; /* Face has 3 vertices */
4753       break;
4754     case 6:                 /* tet cohesive cells */
4755       *numFaceVertices = 4; /* Face has 4 vertices */
4756       break;
4757     case 8:                 /* hexahedron */
4758       *numFaceVertices = 4; /* Face has 4 vertices */
4759       break;
4760     case 9:                 /* tet cohesive Lagrange cells */
4761       *numFaceVertices = 6; /* Face has 6 vertices */
4762       break;
4763     case 10:                /* quadratic tetrahedron */
4764       *numFaceVertices = 6; /* Face has 6 vertices */
4765       break;
4766     case 12:                /* hex cohesive Lagrange cells */
4767       *numFaceVertices = 6; /* Face has 6 vertices */
4768       break;
4769     case 18:                /* quadratic tet cohesive Lagrange cells */
4770       *numFaceVertices = 6; /* Face has 6 vertices */
4771       break;
4772     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4773       *numFaceVertices = 9; /* Face has 9 vertices */
4774       break;
4775     default:
4776       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4777     }
4778     break;
4779   default:
4780     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4781   }
4782   PetscFunctionReturn(0);
4783 }
4784 
4785 /*@
4786   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4787 
4788   Not Collective
4789 
4790   Input Parameter:
4791 . dm    - The `DMPLEX` object
4792 
4793   Output Parameter:
4794 . depthLabel - The `DMLabel` recording point depth
4795 
4796   Level: developer
4797 
4798 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4799 @*/
4800 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4801 {
4802   PetscFunctionBegin;
4803   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4804   PetscValidPointer(depthLabel, 2);
4805   *depthLabel = dm->depthLabel;
4806   PetscFunctionReturn(0);
4807 }
4808 
4809 /*@
4810   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4811 
4812   Not Collective
4813 
4814   Input Parameter:
4815 . dm    - The `DMPLEX` object
4816 
4817   Output Parameter:
4818 . depth - The number of strata (breadth first levels) in the DAG
4819 
4820   Level: developer
4821 
4822   Notes:
4823   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4824 
4825   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4826 
4827   An empty mesh gives -1.
4828 
4829 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4830 @*/
4831 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4832 {
4833   DMLabel  label;
4834   PetscInt d = 0;
4835 
4836   PetscFunctionBegin;
4837   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4838   PetscValidIntPointer(depth, 2);
4839   PetscCall(DMPlexGetDepthLabel(dm, &label));
4840   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4841   *depth = d - 1;
4842   PetscFunctionReturn(0);
4843 }
4844 
4845 /*@
4846   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4847 
4848   Not Collective
4849 
4850   Input Parameters:
4851 + dm    - The `DMPLEX` object
4852 - depth - The requested depth
4853 
4854   Output Parameters:
4855 + start - The first point at this depth
4856 - end   - One beyond the last point at this depth
4857 
4858   Level: developer
4859 
4860   Notes:
4861   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4862   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
4863   higher dimension, e.g., "edges".
4864 
4865 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4866 @*/
4867 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4868 {
4869   DMLabel  label;
4870   PetscInt pStart, pEnd;
4871 
4872   PetscFunctionBegin;
4873   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4874   if (start) {
4875     PetscValidIntPointer(start, 3);
4876     *start = 0;
4877   }
4878   if (end) {
4879     PetscValidIntPointer(end, 4);
4880     *end = 0;
4881   }
4882   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4883   if (pStart == pEnd) PetscFunctionReturn(0);
4884   if (depth < 0) {
4885     if (start) *start = pStart;
4886     if (end) *end = pEnd;
4887     PetscFunctionReturn(0);
4888   }
4889   PetscCall(DMPlexGetDepthLabel(dm, &label));
4890   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4891   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4892   PetscFunctionReturn(0);
4893 }
4894 
4895 /*@
4896   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4897 
4898   Not Collective
4899 
4900   Input Parameters:
4901 + dm     - The `DMPLEX` object
4902 - height - The requested height
4903 
4904   Output Parameters:
4905 + start - The first point at this height
4906 - end   - One beyond the last point at this height
4907 
4908   Level: developer
4909 
4910   Notes:
4911   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4912   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
4913   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4914 
4915 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4916 @*/
4917 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
4918 {
4919   DMLabel  label;
4920   PetscInt depth, pStart, pEnd;
4921 
4922   PetscFunctionBegin;
4923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4924   if (start) {
4925     PetscValidIntPointer(start, 3);
4926     *start = 0;
4927   }
4928   if (end) {
4929     PetscValidIntPointer(end, 4);
4930     *end = 0;
4931   }
4932   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4933   if (pStart == pEnd) PetscFunctionReturn(0);
4934   if (height < 0) {
4935     if (start) *start = pStart;
4936     if (end) *end = pEnd;
4937     PetscFunctionReturn(0);
4938   }
4939   PetscCall(DMPlexGetDepthLabel(dm, &label));
4940   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4941   PetscCall(DMLabelGetNumValues(label, &depth));
4942   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
4943   PetscFunctionReturn(0);
4944 }
4945 
4946 /*@
4947   DMPlexGetPointDepth - Get the depth of a given point
4948 
4949   Not Collective
4950 
4951   Input Parameters:
4952 + dm    - The `DMPLEX` object
4953 - point - The point
4954 
4955   Output Parameter:
4956 . depth - The depth of the point
4957 
4958   Level: intermediate
4959 
4960 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4961 @*/
4962 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
4963 {
4964   PetscFunctionBegin;
4965   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4966   PetscValidIntPointer(depth, 3);
4967   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4968   PetscFunctionReturn(0);
4969 }
4970 
4971 /*@
4972   DMPlexGetPointHeight - Get the height of a given point
4973 
4974   Not Collective
4975 
4976   Input Parameters:
4977 + dm    - The `DMPLEX` object
4978 - point - The point
4979 
4980   Output Parameter:
4981 . height - The height of the point
4982 
4983   Level: intermediate
4984 
4985 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4986 @*/
4987 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
4988 {
4989   PetscInt n, pDepth;
4990 
4991   PetscFunctionBegin;
4992   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4993   PetscValidIntPointer(height, 3);
4994   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4995   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4996   *height = n - 1 - pDepth; /* DAG depth is n-1 */
4997   PetscFunctionReturn(0);
4998 }
4999 
5000 /*@
5001   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5002 
5003   Not Collective
5004 
5005   Input Parameter:
5006 . dm - The `DMPLEX` object
5007 
5008   Output Parameter:
5009 . celltypeLabel - The `DMLabel` recording cell polytope type
5010 
5011   Level: developer
5012 
5013   Note:
5014   This function will trigger automatica computation of cell types. This can be disabled by calling
5015   `DMCreateLabel`(dm, "celltype") beforehand.
5016 
5017 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5018 @*/
5019 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5020 {
5021   PetscFunctionBegin;
5022   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5023   PetscValidPointer(celltypeLabel, 2);
5024   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5025   *celltypeLabel = dm->celltypeLabel;
5026   PetscFunctionReturn(0);
5027 }
5028 
5029 /*@
5030   DMPlexGetCellType - Get the polytope type of a given cell
5031 
5032   Not Collective
5033 
5034   Input Parameters:
5035 + dm   - The `DMPLEX` object
5036 - cell - The cell
5037 
5038   Output Parameter:
5039 . celltype - The polytope type of the cell
5040 
5041   Level: intermediate
5042 
5043 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5044 @*/
5045 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5046 {
5047   DMLabel  label;
5048   PetscInt ct;
5049 
5050   PetscFunctionBegin;
5051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5052   PetscValidPointer(celltype, 3);
5053   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5054   PetscCall(DMLabelGetValue(label, cell, &ct));
5055   PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5056   *celltype = (DMPolytopeType)ct;
5057   PetscFunctionReturn(0);
5058 }
5059 
5060 /*@
5061   DMPlexSetCellType - Set the polytope type of a given cell
5062 
5063   Not Collective
5064 
5065   Input Parameters:
5066 + dm   - The `DMPLEX` object
5067 . cell - The cell
5068 - celltype - The polytope type of the cell
5069 
5070   Level: advanced
5071 
5072   Note:
5073   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5074   is executed. This function will override the computed type. However, if automatic classification will not succeed
5075   and a user wants to manually specify all types, the classification must be disabled by calling
5076   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5077 
5078 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5079 @*/
5080 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5081 {
5082   DMLabel label;
5083 
5084   PetscFunctionBegin;
5085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5086   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5087   PetscCall(DMLabelSetValue(label, cell, celltype));
5088   PetscFunctionReturn(0);
5089 }
5090 
5091 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5092 {
5093   PetscSection section, s;
5094   Mat          m;
5095   PetscInt     maxHeight;
5096 
5097   PetscFunctionBegin;
5098   PetscCall(DMClone(dm, cdm));
5099   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5100   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5101   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5102   PetscCall(DMSetLocalSection(*cdm, section));
5103   PetscCall(PetscSectionDestroy(&section));
5104   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5105   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5106   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5107   PetscCall(PetscSectionDestroy(&s));
5108   PetscCall(MatDestroy(&m));
5109 
5110   PetscCall(DMSetNumFields(*cdm, 1));
5111   PetscCall(DMCreateDS(*cdm));
5112   PetscFunctionReturn(0);
5113 }
5114 
5115 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5116 {
5117   Vec coordsLocal, cellCoordsLocal;
5118   DM  coordsDM, cellCoordsDM;
5119 
5120   PetscFunctionBegin;
5121   *field = NULL;
5122   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5123   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5124   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5125   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5126   if (coordsLocal && coordsDM) {
5127     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5128     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5129   }
5130   PetscFunctionReturn(0);
5131 }
5132 
5133 /*@C
5134   DMPlexGetConeSection - Return a section which describes the layout of cone data
5135 
5136   Not Collective
5137 
5138   Input Parameters:
5139 . dm        - The `DMPLEX` object
5140 
5141   Output Parameter:
5142 . section - The `PetscSection` object
5143 
5144   Level: developer
5145 
5146 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5147 @*/
5148 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5149 {
5150   DM_Plex *mesh = (DM_Plex *)dm->data;
5151 
5152   PetscFunctionBegin;
5153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5154   if (section) *section = mesh->coneSection;
5155   PetscFunctionReturn(0);
5156 }
5157 
5158 /*@C
5159   DMPlexGetSupportSection - Return a section which describes the layout of support data
5160 
5161   Not Collective
5162 
5163   Input Parameters:
5164 . dm        - The `DMPLEX` object
5165 
5166   Output Parameter:
5167 . section - The `PetscSection` object
5168 
5169   Level: developer
5170 
5171 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5172 @*/
5173 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5174 {
5175   DM_Plex *mesh = (DM_Plex *)dm->data;
5176 
5177   PetscFunctionBegin;
5178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5179   if (section) *section = mesh->supportSection;
5180   PetscFunctionReturn(0);
5181 }
5182 
5183 /*@C
5184   DMPlexGetCones - Return cone data
5185 
5186   Not Collective
5187 
5188   Input Parameters:
5189 . dm        - The `DMPLEX` object
5190 
5191   Output Parameter:
5192 . cones - The cone for each point
5193 
5194   Level: developer
5195 
5196 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5197 @*/
5198 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5199 {
5200   DM_Plex *mesh = (DM_Plex *)dm->data;
5201 
5202   PetscFunctionBegin;
5203   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5204   if (cones) *cones = mesh->cones;
5205   PetscFunctionReturn(0);
5206 }
5207 
5208 /*@C
5209   DMPlexGetConeOrientations - Return cone orientation data
5210 
5211   Not Collective
5212 
5213   Input Parameters:
5214 . dm        - The `DMPLEX` object
5215 
5216   Output Parameter:
5217 . coneOrientations - The array of cone orientations for all points
5218 
5219   Level: developer
5220 
5221   Notes:
5222   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5223 
5224   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5225 
5226 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5227 @*/
5228 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5229 {
5230   DM_Plex *mesh = (DM_Plex *)dm->data;
5231 
5232   PetscFunctionBegin;
5233   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5234   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5235   PetscFunctionReturn(0);
5236 }
5237 
5238 /******************************** FEM Support **********************************/
5239 
5240 /*
5241  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5242  representing a line in the section.
5243 */
5244 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5245 {
5246   PetscFunctionBeginHot;
5247   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5248   if (line < 0) {
5249     *k  = 0;
5250     *Nc = 0;
5251   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5252     *k = 1;
5253   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5254     /* An order k SEM disc has k-1 dofs on an edge */
5255     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5256     *k = *k / *Nc + 1;
5257   }
5258   PetscFunctionReturn(0);
5259 }
5260 
5261 /*@
5262 
5263   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5264   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5265   section provided (or the section of the DM).
5266 
5267   Input Parameters:
5268 + dm      - The DM
5269 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5270 - section - The PetscSection to reorder, or NULL for the default section
5271 
5272   Example:
5273   A typical interpolated single-quad mesh might order points as
5274 .vb
5275   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5276 
5277   v4 -- e6 -- v3
5278   |           |
5279   e7    c0    e8
5280   |           |
5281   v1 -- e5 -- v2
5282 .ve
5283 
5284   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5285   dofs in the order of points, e.g.,
5286 .vb
5287     c0 -> [0,1,2,3]
5288     v1 -> [4]
5289     ...
5290     e5 -> [8, 9]
5291 .ve
5292 
5293   which corresponds to the dofs
5294 .vb
5295     6   10  11  7
5296     13  2   3   15
5297     12  0   1   14
5298     4   8   9   5
5299 .ve
5300 
5301   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5302 .vb
5303   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5304 .ve
5305 
5306   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5307 .vb
5308    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5309 .ve
5310 
5311   Level: developer
5312 
5313   Note:
5314   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5315   degree of the basis.
5316 
5317 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5318 @*/
5319 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5320 {
5321   DMLabel   label;
5322   PetscInt  dim, depth = -1, eStart = -1, Nf;
5323   PetscBool vertexchart;
5324 
5325   PetscFunctionBegin;
5326   PetscCall(DMGetDimension(dm, &dim));
5327   if (dim < 1) PetscFunctionReturn(0);
5328   if (point < 0) {
5329     PetscInt sStart, sEnd;
5330 
5331     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5332     point = sEnd - sStart ? sStart : point;
5333   }
5334   PetscCall(DMPlexGetDepthLabel(dm, &label));
5335   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5336   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5337   if (depth == 1) {
5338     eStart = point;
5339   } else if (depth == dim) {
5340     const PetscInt *cone;
5341 
5342     PetscCall(DMPlexGetCone(dm, point, &cone));
5343     if (dim == 2) eStart = cone[0];
5344     else if (dim == 3) {
5345       const PetscInt *cone2;
5346       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5347       eStart = cone2[0];
5348     } 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);
5349   } 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);
5350   { /* Determine whether the chart covers all points or just vertices. */
5351     PetscInt pStart, pEnd, cStart, cEnd;
5352     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5353     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5354     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5355     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5356     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5357   }
5358   PetscCall(PetscSectionGetNumFields(section, &Nf));
5359   for (PetscInt d = 1; d <= dim; d++) {
5360     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5361     PetscInt *perm;
5362 
5363     for (f = 0; f < Nf; ++f) {
5364       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5365       size += PetscPowInt(k + 1, d) * Nc;
5366     }
5367     PetscCall(PetscMalloc1(size, &perm));
5368     for (f = 0; f < Nf; ++f) {
5369       switch (d) {
5370       case 1:
5371         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5372         /*
5373          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5374          We want              [ vtx0; edge of length k-1; vtx1 ]
5375          */
5376         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5377         for (i = 0; i < k - 1; i++)
5378           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5379         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5380         foffset = offset;
5381         break;
5382       case 2:
5383         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5384         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5385         /* The SEM order is
5386 
5387          v_lb, {e_b}, v_rb,
5388          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5389          v_lt, reverse {e_t}, v_rt
5390          */
5391         {
5392           const PetscInt of   = 0;
5393           const PetscInt oeb  = of + PetscSqr(k - 1);
5394           const PetscInt oer  = oeb + (k - 1);
5395           const PetscInt oet  = oer + (k - 1);
5396           const PetscInt oel  = oet + (k - 1);
5397           const PetscInt ovlb = oel + (k - 1);
5398           const PetscInt ovrb = ovlb + 1;
5399           const PetscInt ovrt = ovrb + 1;
5400           const PetscInt ovlt = ovrt + 1;
5401           PetscInt       o;
5402 
5403           /* bottom */
5404           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5405           for (o = oeb; o < oer; ++o)
5406             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5407           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5408           /* middle */
5409           for (i = 0; i < k - 1; ++i) {
5410             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5411             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5412               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5413             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5414           }
5415           /* top */
5416           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5417           for (o = oel - 1; o >= oet; --o)
5418             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5419           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5420           foffset = offset;
5421         }
5422         break;
5423       case 3:
5424         /* The original hex closure is
5425 
5426          {c,
5427          f_b, f_t, f_f, f_b, f_r, f_l,
5428          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5429          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5430          */
5431         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5432         /* The SEM order is
5433          Bottom Slice
5434          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5435          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5436          v_blb, {e_bb}, v_brb,
5437 
5438          Middle Slice (j)
5439          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5440          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5441          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5442 
5443          Top Slice
5444          v_tlf, {e_tf}, v_trf,
5445          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5446          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5447          */
5448         {
5449           const PetscInt oc    = 0;
5450           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5451           const PetscInt oft   = ofb + PetscSqr(k - 1);
5452           const PetscInt off   = oft + PetscSqr(k - 1);
5453           const PetscInt ofk   = off + PetscSqr(k - 1);
5454           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5455           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5456           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5457           const PetscInt oebb  = oebl + (k - 1);
5458           const PetscInt oebr  = oebb + (k - 1);
5459           const PetscInt oebf  = oebr + (k - 1);
5460           const PetscInt oetf  = oebf + (k - 1);
5461           const PetscInt oetr  = oetf + (k - 1);
5462           const PetscInt oetb  = oetr + (k - 1);
5463           const PetscInt oetl  = oetb + (k - 1);
5464           const PetscInt oerf  = oetl + (k - 1);
5465           const PetscInt oelf  = oerf + (k - 1);
5466           const PetscInt oelb  = oelf + (k - 1);
5467           const PetscInt oerb  = oelb + (k - 1);
5468           const PetscInt ovblf = oerb + (k - 1);
5469           const PetscInt ovblb = ovblf + 1;
5470           const PetscInt ovbrb = ovblb + 1;
5471           const PetscInt ovbrf = ovbrb + 1;
5472           const PetscInt ovtlf = ovbrf + 1;
5473           const PetscInt ovtrf = ovtlf + 1;
5474           const PetscInt ovtrb = ovtrf + 1;
5475           const PetscInt ovtlb = ovtrb + 1;
5476           PetscInt       o, n;
5477 
5478           /* Bottom Slice */
5479           /*   bottom */
5480           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5481           for (o = oetf - 1; o >= oebf; --o)
5482             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5483           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5484           /*   middle */
5485           for (i = 0; i < k - 1; ++i) {
5486             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5487             for (n = 0; n < k - 1; ++n) {
5488               o = ofb + n * (k - 1) + i;
5489               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5490             }
5491             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5492           }
5493           /*   top */
5494           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5495           for (o = oebb; o < oebr; ++o)
5496             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5497           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5498 
5499           /* Middle Slice */
5500           for (j = 0; j < k - 1; ++j) {
5501             /*   bottom */
5502             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5503             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5504               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5505             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5506             /*   middle */
5507             for (i = 0; i < k - 1; ++i) {
5508               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5509               for (n = 0; n < k - 1; ++n)
5510                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5511               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5512             }
5513             /*   top */
5514             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5515             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5516               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5517             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5518           }
5519 
5520           /* Top Slice */
5521           /*   bottom */
5522           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5523           for (o = oetf; o < oetr; ++o)
5524             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5525           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5526           /*   middle */
5527           for (i = 0; i < k - 1; ++i) {
5528             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5529             for (n = 0; n < k - 1; ++n)
5530               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5531             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5532           }
5533           /*   top */
5534           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5535           for (o = oetl - 1; o >= oetb; --o)
5536             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5537           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5538 
5539           foffset = offset;
5540         }
5541         break;
5542       default:
5543         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5544       }
5545     }
5546     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5547     /* Check permutation */
5548     {
5549       PetscInt *check;
5550 
5551       PetscCall(PetscMalloc1(size, &check));
5552       for (i = 0; i < size; ++i) {
5553         check[i] = -1;
5554         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5555       }
5556       for (i = 0; i < size; ++i) check[perm[i]] = i;
5557       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5558       PetscCall(PetscFree(check));
5559     }
5560     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5561     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5562       PetscInt *loc_perm;
5563       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5564       for (PetscInt i = 0; i < size; i++) {
5565         loc_perm[i]        = perm[i];
5566         loc_perm[size + i] = size + perm[i];
5567       }
5568       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5569     }
5570   }
5571   PetscFunctionReturn(0);
5572 }
5573 
5574 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5575 {
5576   PetscDS  prob;
5577   PetscInt depth, Nf, h;
5578   DMLabel  label;
5579 
5580   PetscFunctionBeginHot;
5581   PetscCall(DMGetDS(dm, &prob));
5582   Nf      = prob->Nf;
5583   label   = dm->depthLabel;
5584   *dspace = NULL;
5585   if (field < Nf) {
5586     PetscObject disc = prob->disc[field];
5587 
5588     if (disc->classid == PETSCFE_CLASSID) {
5589       PetscDualSpace dsp;
5590 
5591       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5592       PetscCall(DMLabelGetNumValues(label, &depth));
5593       PetscCall(DMLabelGetValue(label, point, &h));
5594       h = depth - 1 - h;
5595       if (h) {
5596         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5597       } else {
5598         *dspace = dsp;
5599       }
5600     }
5601   }
5602   PetscFunctionReturn(0);
5603 }
5604 
5605 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5606 {
5607   PetscScalar       *array;
5608   const PetscScalar *vArray;
5609   const PetscInt    *cone, *coneO;
5610   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5611 
5612   PetscFunctionBeginHot;
5613   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5614   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5615   PetscCall(DMPlexGetCone(dm, point, &cone));
5616   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5617   if (!values || !*values) {
5618     if ((point >= pStart) && (point < pEnd)) {
5619       PetscInt dof;
5620 
5621       PetscCall(PetscSectionGetDof(section, point, &dof));
5622       size += dof;
5623     }
5624     for (p = 0; p < numPoints; ++p) {
5625       const PetscInt cp = cone[p];
5626       PetscInt       dof;
5627 
5628       if ((cp < pStart) || (cp >= pEnd)) continue;
5629       PetscCall(PetscSectionGetDof(section, cp, &dof));
5630       size += dof;
5631     }
5632     if (!values) {
5633       if (csize) *csize = size;
5634       PetscFunctionReturn(0);
5635     }
5636     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5637   } else {
5638     array = *values;
5639   }
5640   size = 0;
5641   PetscCall(VecGetArrayRead(v, &vArray));
5642   if ((point >= pStart) && (point < pEnd)) {
5643     PetscInt           dof, off, d;
5644     const PetscScalar *varr;
5645 
5646     PetscCall(PetscSectionGetDof(section, point, &dof));
5647     PetscCall(PetscSectionGetOffset(section, point, &off));
5648     varr = &vArray[off];
5649     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5650     size += dof;
5651   }
5652   for (p = 0; p < numPoints; ++p) {
5653     const PetscInt     cp = cone[p];
5654     PetscInt           o  = coneO[p];
5655     PetscInt           dof, off, d;
5656     const PetscScalar *varr;
5657 
5658     if ((cp < pStart) || (cp >= pEnd)) continue;
5659     PetscCall(PetscSectionGetDof(section, cp, &dof));
5660     PetscCall(PetscSectionGetOffset(section, cp, &off));
5661     varr = &vArray[off];
5662     if (o >= 0) {
5663       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5664     } else {
5665       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5666     }
5667     size += dof;
5668   }
5669   PetscCall(VecRestoreArrayRead(v, &vArray));
5670   if (!*values) {
5671     if (csize) *csize = size;
5672     *values = array;
5673   } else {
5674     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5675     *csize = size;
5676   }
5677   PetscFunctionReturn(0);
5678 }
5679 
5680 /* Compress out points not in the section */
5681 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5682 {
5683   const PetscInt np = *numPoints;
5684   PetscInt       pStart, pEnd, p, q;
5685 
5686   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5687   for (p = 0, q = 0; p < np; ++p) {
5688     const PetscInt r = points[p * 2];
5689     if ((r >= pStart) && (r < pEnd)) {
5690       points[q * 2]     = r;
5691       points[q * 2 + 1] = points[p * 2 + 1];
5692       ++q;
5693     }
5694   }
5695   *numPoints = q;
5696   return 0;
5697 }
5698 
5699 /* Compressed closure does not apply closure permutation */
5700 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5701 {
5702   const PetscInt *cla = NULL;
5703   PetscInt        np, *pts = NULL;
5704 
5705   PetscFunctionBeginHot;
5706   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5707   if (*clPoints) {
5708     PetscInt dof, off;
5709 
5710     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5711     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5712     PetscCall(ISGetIndices(*clPoints, &cla));
5713     np  = dof / 2;
5714     pts = (PetscInt *)&cla[off];
5715   } else {
5716     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5717     PetscCall(CompressPoints_Private(section, &np, pts));
5718   }
5719   *numPoints = np;
5720   *points    = pts;
5721   *clp       = cla;
5722   PetscFunctionReturn(0);
5723 }
5724 
5725 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5726 {
5727   PetscFunctionBeginHot;
5728   if (!*clPoints) {
5729     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5730   } else {
5731     PetscCall(ISRestoreIndices(*clPoints, clp));
5732   }
5733   *numPoints = 0;
5734   *points    = NULL;
5735   *clSec     = NULL;
5736   *clPoints  = NULL;
5737   *clp       = NULL;
5738   PetscFunctionReturn(0);
5739 }
5740 
5741 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5742 {
5743   PetscInt            offset = 0, p;
5744   const PetscInt    **perms  = NULL;
5745   const PetscScalar **flips  = NULL;
5746 
5747   PetscFunctionBeginHot;
5748   *size = 0;
5749   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5750   for (p = 0; p < numPoints; p++) {
5751     const PetscInt     point = points[2 * p];
5752     const PetscInt    *perm  = perms ? perms[p] : NULL;
5753     const PetscScalar *flip  = flips ? flips[p] : NULL;
5754     PetscInt           dof, off, d;
5755     const PetscScalar *varr;
5756 
5757     PetscCall(PetscSectionGetDof(section, point, &dof));
5758     PetscCall(PetscSectionGetOffset(section, point, &off));
5759     varr = &vArray[off];
5760     if (clperm) {
5761       if (perm) {
5762         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5763       } else {
5764         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5765       }
5766       if (flip) {
5767         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5768       }
5769     } else {
5770       if (perm) {
5771         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5772       } else {
5773         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5774       }
5775       if (flip) {
5776         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5777       }
5778     }
5779     offset += dof;
5780   }
5781   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5782   *size = offset;
5783   PetscFunctionReturn(0);
5784 }
5785 
5786 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[])
5787 {
5788   PetscInt offset = 0, f;
5789 
5790   PetscFunctionBeginHot;
5791   *size = 0;
5792   for (f = 0; f < numFields; ++f) {
5793     PetscInt            p;
5794     const PetscInt    **perms = NULL;
5795     const PetscScalar **flips = NULL;
5796 
5797     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5798     for (p = 0; p < numPoints; p++) {
5799       const PetscInt     point = points[2 * p];
5800       PetscInt           fdof, foff, b;
5801       const PetscScalar *varr;
5802       const PetscInt    *perm = perms ? perms[p] : NULL;
5803       const PetscScalar *flip = flips ? flips[p] : NULL;
5804 
5805       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5806       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5807       varr = &vArray[foff];
5808       if (clperm) {
5809         if (perm) {
5810           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5811         } else {
5812           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5813         }
5814         if (flip) {
5815           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5816         }
5817       } else {
5818         if (perm) {
5819           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5820         } else {
5821           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5822         }
5823         if (flip) {
5824           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5825         }
5826       }
5827       offset += fdof;
5828     }
5829     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5830   }
5831   *size = offset;
5832   PetscFunctionReturn(0);
5833 }
5834 
5835 /*@C
5836   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5837 
5838   Not collective
5839 
5840   Input Parameters:
5841 + dm - The `DM`
5842 . section - The section describing the layout in v, or NULL to use the default section
5843 . v - The local vector
5844 - point - The point in the `DM`
5845 
5846   Input/Output Parameters:
5847 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5848 - values - An array to use for the values, or NULL to have it allocated automatically;
5849            if the user provided NULL, it is a borrowed array and should not be freed
5850 
5851 
5852   Level: intermediate
5853 
5854   Notes:
5855   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to NULL in the
5856   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
5857   assembly function, and a user may already have allocated storage for this operation.
5858 
5859   A typical use could be
5860 .vb
5861    values = NULL;
5862    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5863    for (cl = 0; cl < clSize; ++cl) {
5864      <Compute on closure>
5865    }
5866    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5867 .ve
5868   or
5869 .vb
5870    PetscMalloc1(clMaxSize, &values);
5871    for (p = pStart; p < pEnd; ++p) {
5872      clSize = clMaxSize;
5873      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5874      for (cl = 0; cl < clSize; ++cl) {
5875        <Compute on closure>
5876      }
5877    }
5878    PetscFree(values);
5879 .ve
5880 
5881   Fortran Note:
5882   The csize argument is not present in the Fortran binding since it is internal to the array.
5883 
5884 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5885 @*/
5886 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5887 {
5888   PetscSection    clSection;
5889   IS              clPoints;
5890   PetscInt       *points = NULL;
5891   const PetscInt *clp, *perm;
5892   PetscInt        depth, numFields, numPoints, asize;
5893 
5894   PetscFunctionBeginHot;
5895   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5896   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5897   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5898   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5899   PetscCall(DMPlexGetDepth(dm, &depth));
5900   PetscCall(PetscSectionGetNumFields(section, &numFields));
5901   if (depth == 1 && numFields < 2) {
5902     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5903     PetscFunctionReturn(0);
5904   }
5905   /* Get points */
5906   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5907   /* Get sizes */
5908   asize = 0;
5909   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
5910     PetscInt dof;
5911     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5912     asize += dof;
5913   }
5914   if (values) {
5915     const PetscScalar *vArray;
5916     PetscInt           size;
5917 
5918     if (*values) {
5919       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);
5920     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5921     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
5922     PetscCall(VecGetArrayRead(v, &vArray));
5923     /* Get values */
5924     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5925     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5926     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5927     /* Cleanup array */
5928     PetscCall(VecRestoreArrayRead(v, &vArray));
5929   }
5930   if (csize) *csize = asize;
5931   /* Cleanup points */
5932   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5933   PetscFunctionReturn(0);
5934 }
5935 
5936 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
5937 {
5938   DMLabel            depthLabel;
5939   PetscSection       clSection;
5940   IS                 clPoints;
5941   PetscScalar       *array;
5942   const PetscScalar *vArray;
5943   PetscInt          *points = NULL;
5944   const PetscInt    *clp, *perm = NULL;
5945   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5946 
5947   PetscFunctionBeginHot;
5948   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5949   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5950   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5951   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5952   PetscCall(DMPlexGetDepth(dm, &mdepth));
5953   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5954   PetscCall(PetscSectionGetNumFields(section, &numFields));
5955   if (mdepth == 1 && numFields < 2) {
5956     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5957     PetscFunctionReturn(0);
5958   }
5959   /* Get points */
5960   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5961   for (clsize = 0, p = 0; p < Np; p++) {
5962     PetscInt dof;
5963     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
5964     clsize += dof;
5965   }
5966   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
5967   /* Filter points */
5968   for (p = 0; p < numPoints * 2; p += 2) {
5969     PetscInt dep;
5970 
5971     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5972     if (dep != depth) continue;
5973     points[Np * 2 + 0] = points[p];
5974     points[Np * 2 + 1] = points[p + 1];
5975     ++Np;
5976   }
5977   /* Get array */
5978   if (!values || !*values) {
5979     PetscInt asize = 0, dof;
5980 
5981     for (p = 0; p < Np * 2; p += 2) {
5982       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5983       asize += dof;
5984     }
5985     if (!values) {
5986       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5987       if (csize) *csize = asize;
5988       PetscFunctionReturn(0);
5989     }
5990     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5991   } else {
5992     array = *values;
5993   }
5994   PetscCall(VecGetArrayRead(v, &vArray));
5995   /* Get values */
5996   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5997   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5998   /* Cleanup points */
5999   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6000   /* Cleanup array */
6001   PetscCall(VecRestoreArrayRead(v, &vArray));
6002   if (!*values) {
6003     if (csize) *csize = size;
6004     *values = array;
6005   } else {
6006     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6007     *csize = size;
6008   }
6009   PetscFunctionReturn(0);
6010 }
6011 
6012 /*@C
6013   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6014 
6015   Not collective
6016 
6017   Input Parameters:
6018 + dm - The `DM`
6019 . section - The section describing the layout in v, or NULL to use the default section
6020 . v - The local vector
6021 . point - The point in the `DM`
6022 . csize - The number of values in the closure, or NULL
6023 - values - The array of values, which is a borrowed array and should not be freed
6024 
6025   Level: intermediate
6026 
6027   Note:
6028   The array values are discarded and not copied back into v. In order to copy values back to v, use `DMPlexVecSetClosure()`
6029 
6030   Fortran Note:
6031   The csize argument is not present in the Fortran binding since it is internal to the array.
6032 
6033 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6034 @*/
6035 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6036 {
6037   PetscInt size = 0;
6038 
6039   PetscFunctionBegin;
6040   /* Should work without recalculating size */
6041   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6042   *values = NULL;
6043   PetscFunctionReturn(0);
6044 }
6045 
6046 static inline void add(PetscScalar *x, PetscScalar y)
6047 {
6048   *x += y;
6049 }
6050 static inline void insert(PetscScalar *x, PetscScalar y)
6051 {
6052   *x = y;
6053 }
6054 
6055 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[])
6056 {
6057   PetscInt        cdof;  /* The number of constraints on this point */
6058   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6059   PetscScalar    *a;
6060   PetscInt        off, cind = 0, k;
6061 
6062   PetscFunctionBegin;
6063   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6064   PetscCall(PetscSectionGetOffset(section, point, &off));
6065   a = &array[off];
6066   if (!cdof || setBC) {
6067     if (clperm) {
6068       if (perm) {
6069         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6070       } else {
6071         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6072       }
6073     } else {
6074       if (perm) {
6075         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6076       } else {
6077         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6078       }
6079     }
6080   } else {
6081     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6082     if (clperm) {
6083       if (perm) {
6084         for (k = 0; k < dof; ++k) {
6085           if ((cind < cdof) && (k == cdofs[cind])) {
6086             ++cind;
6087             continue;
6088           }
6089           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6090         }
6091       } else {
6092         for (k = 0; k < dof; ++k) {
6093           if ((cind < cdof) && (k == cdofs[cind])) {
6094             ++cind;
6095             continue;
6096           }
6097           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6098         }
6099       }
6100     } else {
6101       if (perm) {
6102         for (k = 0; k < dof; ++k) {
6103           if ((cind < cdof) && (k == cdofs[cind])) {
6104             ++cind;
6105             continue;
6106           }
6107           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6108         }
6109       } else {
6110         for (k = 0; k < dof; ++k) {
6111           if ((cind < cdof) && (k == cdofs[cind])) {
6112             ++cind;
6113             continue;
6114           }
6115           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6116         }
6117       }
6118     }
6119   }
6120   PetscFunctionReturn(0);
6121 }
6122 
6123 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[])
6124 {
6125   PetscInt        cdof;  /* The number of constraints on this point */
6126   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6127   PetscScalar    *a;
6128   PetscInt        off, cind = 0, k;
6129 
6130   PetscFunctionBegin;
6131   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6132   PetscCall(PetscSectionGetOffset(section, point, &off));
6133   a = &array[off];
6134   if (cdof) {
6135     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6136     if (clperm) {
6137       if (perm) {
6138         for (k = 0; k < dof; ++k) {
6139           if ((cind < cdof) && (k == cdofs[cind])) {
6140             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6141             cind++;
6142           }
6143         }
6144       } else {
6145         for (k = 0; k < dof; ++k) {
6146           if ((cind < cdof) && (k == cdofs[cind])) {
6147             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6148             cind++;
6149           }
6150         }
6151       }
6152     } else {
6153       if (perm) {
6154         for (k = 0; k < dof; ++k) {
6155           if ((cind < cdof) && (k == cdofs[cind])) {
6156             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6157             cind++;
6158           }
6159         }
6160       } else {
6161         for (k = 0; k < dof; ++k) {
6162           if ((cind < cdof) && (k == cdofs[cind])) {
6163             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6164             cind++;
6165           }
6166         }
6167       }
6168     }
6169   }
6170   PetscFunctionReturn(0);
6171 }
6172 
6173 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[])
6174 {
6175   PetscScalar    *a;
6176   PetscInt        fdof, foff, fcdof, foffset = *offset;
6177   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6178   PetscInt        cind = 0, b;
6179 
6180   PetscFunctionBegin;
6181   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6182   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6183   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6184   a = &array[foff];
6185   if (!fcdof || setBC) {
6186     if (clperm) {
6187       if (perm) {
6188         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6189       } else {
6190         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6191       }
6192     } else {
6193       if (perm) {
6194         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6195       } else {
6196         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6197       }
6198     }
6199   } else {
6200     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6201     if (clperm) {
6202       if (perm) {
6203         for (b = 0; b < fdof; b++) {
6204           if ((cind < fcdof) && (b == fcdofs[cind])) {
6205             ++cind;
6206             continue;
6207           }
6208           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6209         }
6210       } else {
6211         for (b = 0; b < fdof; b++) {
6212           if ((cind < fcdof) && (b == fcdofs[cind])) {
6213             ++cind;
6214             continue;
6215           }
6216           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6217         }
6218       }
6219     } else {
6220       if (perm) {
6221         for (b = 0; b < fdof; b++) {
6222           if ((cind < fcdof) && (b == fcdofs[cind])) {
6223             ++cind;
6224             continue;
6225           }
6226           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6227         }
6228       } else {
6229         for (b = 0; b < fdof; b++) {
6230           if ((cind < fcdof) && (b == fcdofs[cind])) {
6231             ++cind;
6232             continue;
6233           }
6234           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6235         }
6236       }
6237     }
6238   }
6239   *offset += fdof;
6240   PetscFunctionReturn(0);
6241 }
6242 
6243 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[])
6244 {
6245   PetscScalar    *a;
6246   PetscInt        fdof, foff, fcdof, foffset = *offset;
6247   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6248   PetscInt        Nc, cind = 0, ncind = 0, b;
6249   PetscBool       ncSet, fcSet;
6250 
6251   PetscFunctionBegin;
6252   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6253   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6254   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6255   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6256   a = &array[foff];
6257   if (fcdof) {
6258     /* We just override fcdof and fcdofs with Ncc and comps */
6259     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6260     if (clperm) {
6261       if (perm) {
6262         if (comps) {
6263           for (b = 0; b < fdof; b++) {
6264             ncSet = fcSet = PETSC_FALSE;
6265             if (b % Nc == comps[ncind]) {
6266               ncind = (ncind + 1) % Ncc;
6267               ncSet = PETSC_TRUE;
6268             }
6269             if ((cind < fcdof) && (b == fcdofs[cind])) {
6270               ++cind;
6271               fcSet = PETSC_TRUE;
6272             }
6273             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6274           }
6275         } else {
6276           for (b = 0; b < fdof; b++) {
6277             if ((cind < fcdof) && (b == fcdofs[cind])) {
6278               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6279               ++cind;
6280             }
6281           }
6282         }
6283       } else {
6284         if (comps) {
6285           for (b = 0; b < fdof; b++) {
6286             ncSet = fcSet = PETSC_FALSE;
6287             if (b % Nc == comps[ncind]) {
6288               ncind = (ncind + 1) % Ncc;
6289               ncSet = PETSC_TRUE;
6290             }
6291             if ((cind < fcdof) && (b == fcdofs[cind])) {
6292               ++cind;
6293               fcSet = PETSC_TRUE;
6294             }
6295             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6296           }
6297         } else {
6298           for (b = 0; b < fdof; b++) {
6299             if ((cind < fcdof) && (b == fcdofs[cind])) {
6300               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6301               ++cind;
6302             }
6303           }
6304         }
6305       }
6306     } else {
6307       if (perm) {
6308         if (comps) {
6309           for (b = 0; b < fdof; b++) {
6310             ncSet = fcSet = PETSC_FALSE;
6311             if (b % Nc == comps[ncind]) {
6312               ncind = (ncind + 1) % Ncc;
6313               ncSet = PETSC_TRUE;
6314             }
6315             if ((cind < fcdof) && (b == fcdofs[cind])) {
6316               ++cind;
6317               fcSet = PETSC_TRUE;
6318             }
6319             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6320           }
6321         } else {
6322           for (b = 0; b < fdof; b++) {
6323             if ((cind < fcdof) && (b == fcdofs[cind])) {
6324               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6325               ++cind;
6326             }
6327           }
6328         }
6329       } else {
6330         if (comps) {
6331           for (b = 0; b < fdof; b++) {
6332             ncSet = fcSet = PETSC_FALSE;
6333             if (b % Nc == comps[ncind]) {
6334               ncind = (ncind + 1) % Ncc;
6335               ncSet = PETSC_TRUE;
6336             }
6337             if ((cind < fcdof) && (b == fcdofs[cind])) {
6338               ++cind;
6339               fcSet = PETSC_TRUE;
6340             }
6341             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6342           }
6343         } else {
6344           for (b = 0; b < fdof; b++) {
6345             if ((cind < fcdof) && (b == fcdofs[cind])) {
6346               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6347               ++cind;
6348             }
6349           }
6350         }
6351       }
6352     }
6353   }
6354   *offset += fdof;
6355   PetscFunctionReturn(0);
6356 }
6357 
6358 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6359 {
6360   PetscScalar    *array;
6361   const PetscInt *cone, *coneO;
6362   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6363 
6364   PetscFunctionBeginHot;
6365   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6366   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6367   PetscCall(DMPlexGetCone(dm, point, &cone));
6368   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6369   PetscCall(VecGetArray(v, &array));
6370   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6371     const PetscInt cp = !p ? point : cone[p - 1];
6372     const PetscInt o  = !p ? 0 : coneO[p - 1];
6373 
6374     if ((cp < pStart) || (cp >= pEnd)) {
6375       dof = 0;
6376       continue;
6377     }
6378     PetscCall(PetscSectionGetDof(section, cp, &dof));
6379     /* ADD_VALUES */
6380     {
6381       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6382       PetscScalar    *a;
6383       PetscInt        cdof, coff, cind = 0, k;
6384 
6385       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6386       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6387       a = &array[coff];
6388       if (!cdof) {
6389         if (o >= 0) {
6390           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6391         } else {
6392           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6393         }
6394       } else {
6395         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6396         if (o >= 0) {
6397           for (k = 0; k < dof; ++k) {
6398             if ((cind < cdof) && (k == cdofs[cind])) {
6399               ++cind;
6400               continue;
6401             }
6402             a[k] += values[off + k];
6403           }
6404         } else {
6405           for (k = 0; k < dof; ++k) {
6406             if ((cind < cdof) && (k == cdofs[cind])) {
6407               ++cind;
6408               continue;
6409             }
6410             a[k] += values[off + dof - k - 1];
6411           }
6412         }
6413       }
6414     }
6415   }
6416   PetscCall(VecRestoreArray(v, &array));
6417   PetscFunctionReturn(0);
6418 }
6419 
6420 /*@C
6421   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6422 
6423   Not collective
6424 
6425   Input Parameters:
6426 + dm - The `DM`
6427 . section - The section describing the layout in v, or NULL to use the default section
6428 . v - The local vector
6429 . point - The point in the DM
6430 . values - The array of values
6431 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6432          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6433 
6434   Level: intermediate
6435 
6436 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6437 @*/
6438 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6439 {
6440   PetscSection    clSection;
6441   IS              clPoints;
6442   PetscScalar    *array;
6443   PetscInt       *points = NULL;
6444   const PetscInt *clp, *clperm = NULL;
6445   PetscInt        depth, numFields, numPoints, p, clsize;
6446 
6447   PetscFunctionBeginHot;
6448   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6449   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6450   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6451   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6452   PetscCall(DMPlexGetDepth(dm, &depth));
6453   PetscCall(PetscSectionGetNumFields(section, &numFields));
6454   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6455     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6456     PetscFunctionReturn(0);
6457   }
6458   /* Get points */
6459   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6460   for (clsize = 0, p = 0; p < numPoints; p++) {
6461     PetscInt dof;
6462     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6463     clsize += dof;
6464   }
6465   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6466   /* Get array */
6467   PetscCall(VecGetArray(v, &array));
6468   /* Get values */
6469   if (numFields > 0) {
6470     PetscInt offset = 0, f;
6471     for (f = 0; f < numFields; ++f) {
6472       const PetscInt    **perms = NULL;
6473       const PetscScalar **flips = NULL;
6474 
6475       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6476       switch (mode) {
6477       case INSERT_VALUES:
6478         for (p = 0; p < numPoints; p++) {
6479           const PetscInt     point = points[2 * p];
6480           const PetscInt    *perm  = perms ? perms[p] : NULL;
6481           const PetscScalar *flip  = flips ? flips[p] : NULL;
6482           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6483         }
6484         break;
6485       case INSERT_ALL_VALUES:
6486         for (p = 0; p < numPoints; p++) {
6487           const PetscInt     point = points[2 * p];
6488           const PetscInt    *perm  = perms ? perms[p] : NULL;
6489           const PetscScalar *flip  = flips ? flips[p] : NULL;
6490           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6491         }
6492         break;
6493       case INSERT_BC_VALUES:
6494         for (p = 0; p < numPoints; p++) {
6495           const PetscInt     point = points[2 * p];
6496           const PetscInt    *perm  = perms ? perms[p] : NULL;
6497           const PetscScalar *flip  = flips ? flips[p] : NULL;
6498           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6499         }
6500         break;
6501       case ADD_VALUES:
6502         for (p = 0; p < numPoints; p++) {
6503           const PetscInt     point = points[2 * p];
6504           const PetscInt    *perm  = perms ? perms[p] : NULL;
6505           const PetscScalar *flip  = flips ? flips[p] : NULL;
6506           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6507         }
6508         break;
6509       case ADD_ALL_VALUES:
6510         for (p = 0; p < numPoints; p++) {
6511           const PetscInt     point = points[2 * p];
6512           const PetscInt    *perm  = perms ? perms[p] : NULL;
6513           const PetscScalar *flip  = flips ? flips[p] : NULL;
6514           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6515         }
6516         break;
6517       case ADD_BC_VALUES:
6518         for (p = 0; p < numPoints; p++) {
6519           const PetscInt     point = points[2 * p];
6520           const PetscInt    *perm  = perms ? perms[p] : NULL;
6521           const PetscScalar *flip  = flips ? flips[p] : NULL;
6522           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6523         }
6524         break;
6525       default:
6526         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6527       }
6528       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6529     }
6530   } else {
6531     PetscInt            dof, off;
6532     const PetscInt    **perms = NULL;
6533     const PetscScalar **flips = NULL;
6534 
6535     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6536     switch (mode) {
6537     case INSERT_VALUES:
6538       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6539         const PetscInt     point = points[2 * p];
6540         const PetscInt    *perm  = perms ? perms[p] : NULL;
6541         const PetscScalar *flip  = flips ? flips[p] : NULL;
6542         PetscCall(PetscSectionGetDof(section, point, &dof));
6543         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6544       }
6545       break;
6546     case INSERT_ALL_VALUES:
6547       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6548         const PetscInt     point = points[2 * p];
6549         const PetscInt    *perm  = perms ? perms[p] : NULL;
6550         const PetscScalar *flip  = flips ? flips[p] : NULL;
6551         PetscCall(PetscSectionGetDof(section, point, &dof));
6552         updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array);
6553       }
6554       break;
6555     case INSERT_BC_VALUES:
6556       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6557         const PetscInt     point = points[2 * p];
6558         const PetscInt    *perm  = perms ? perms[p] : NULL;
6559         const PetscScalar *flip  = flips ? flips[p] : NULL;
6560         PetscCall(PetscSectionGetDof(section, point, &dof));
6561         updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array);
6562       }
6563       break;
6564     case ADD_VALUES:
6565       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6566         const PetscInt     point = points[2 * p];
6567         const PetscInt    *perm  = perms ? perms[p] : NULL;
6568         const PetscScalar *flip  = flips ? flips[p] : NULL;
6569         PetscCall(PetscSectionGetDof(section, point, &dof));
6570         updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array);
6571       }
6572       break;
6573     case ADD_ALL_VALUES:
6574       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6575         const PetscInt     point = points[2 * p];
6576         const PetscInt    *perm  = perms ? perms[p] : NULL;
6577         const PetscScalar *flip  = flips ? flips[p] : NULL;
6578         PetscCall(PetscSectionGetDof(section, point, &dof));
6579         updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array);
6580       }
6581       break;
6582     case ADD_BC_VALUES:
6583       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6584         const PetscInt     point = points[2 * p];
6585         const PetscInt    *perm  = perms ? perms[p] : NULL;
6586         const PetscScalar *flip  = flips ? flips[p] : NULL;
6587         PetscCall(PetscSectionGetDof(section, point, &dof));
6588         updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array);
6589       }
6590       break;
6591     default:
6592       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6593     }
6594     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6595   }
6596   /* Cleanup points */
6597   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6598   /* Cleanup array */
6599   PetscCall(VecRestoreArray(v, &array));
6600   PetscFunctionReturn(0);
6601 }
6602 
6603 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6604 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6605 {
6606   PetscFunctionBegin;
6607   *contains = PETSC_TRUE;
6608   if (label) {
6609     PetscInt fdof;
6610 
6611     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6612     if (!*contains) {
6613       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6614       *offset += fdof;
6615       PetscFunctionReturn(0);
6616     }
6617   }
6618   PetscFunctionReturn(0);
6619 }
6620 
6621 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6622 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)
6623 {
6624   PetscSection    clSection;
6625   IS              clPoints;
6626   PetscScalar    *array;
6627   PetscInt       *points = NULL;
6628   const PetscInt *clp;
6629   PetscInt        numFields, numPoints, p;
6630   PetscInt        offset = 0, f;
6631 
6632   PetscFunctionBeginHot;
6633   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6634   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6635   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6636   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6637   PetscCall(PetscSectionGetNumFields(section, &numFields));
6638   /* Get points */
6639   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6640   /* Get array */
6641   PetscCall(VecGetArray(v, &array));
6642   /* Get values */
6643   for (f = 0; f < numFields; ++f) {
6644     const PetscInt    **perms = NULL;
6645     const PetscScalar **flips = NULL;
6646     PetscBool           contains;
6647 
6648     if (!fieldActive[f]) {
6649       for (p = 0; p < numPoints * 2; p += 2) {
6650         PetscInt fdof;
6651         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6652         offset += fdof;
6653       }
6654       continue;
6655     }
6656     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6657     switch (mode) {
6658     case INSERT_VALUES:
6659       for (p = 0; p < numPoints; p++) {
6660         const PetscInt     point = points[2 * p];
6661         const PetscInt    *perm  = perms ? perms[p] : NULL;
6662         const PetscScalar *flip  = flips ? flips[p] : NULL;
6663         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6664         if (!contains) continue;
6665         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6666       }
6667       break;
6668     case INSERT_ALL_VALUES:
6669       for (p = 0; p < numPoints; p++) {
6670         const PetscInt     point = points[2 * p];
6671         const PetscInt    *perm  = perms ? perms[p] : NULL;
6672         const PetscScalar *flip  = flips ? flips[p] : NULL;
6673         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6674         if (!contains) continue;
6675         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6676       }
6677       break;
6678     case INSERT_BC_VALUES:
6679       for (p = 0; p < numPoints; p++) {
6680         const PetscInt     point = points[2 * p];
6681         const PetscInt    *perm  = perms ? perms[p] : NULL;
6682         const PetscScalar *flip  = flips ? flips[p] : NULL;
6683         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6684         if (!contains) continue;
6685         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6686       }
6687       break;
6688     case ADD_VALUES:
6689       for (p = 0; p < numPoints; p++) {
6690         const PetscInt     point = points[2 * p];
6691         const PetscInt    *perm  = perms ? perms[p] : NULL;
6692         const PetscScalar *flip  = flips ? flips[p] : NULL;
6693         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6694         if (!contains) continue;
6695         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6696       }
6697       break;
6698     case ADD_ALL_VALUES:
6699       for (p = 0; p < numPoints; p++) {
6700         const PetscInt     point = points[2 * p];
6701         const PetscInt    *perm  = perms ? perms[p] : NULL;
6702         const PetscScalar *flip  = flips ? flips[p] : NULL;
6703         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6704         if (!contains) continue;
6705         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6706       }
6707       break;
6708     default:
6709       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6710     }
6711     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6712   }
6713   /* Cleanup points */
6714   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6715   /* Cleanup array */
6716   PetscCall(VecRestoreArray(v, &array));
6717   PetscFunctionReturn(0);
6718 }
6719 
6720 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6721 {
6722   PetscMPIInt rank;
6723   PetscInt    i, j;
6724 
6725   PetscFunctionBegin;
6726   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6727   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6728   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6729   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6730   numCIndices = numCIndices ? numCIndices : numRIndices;
6731   if (!values) PetscFunctionReturn(0);
6732   for (i = 0; i < numRIndices; i++) {
6733     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6734     for (j = 0; j < numCIndices; j++) {
6735 #if defined(PETSC_USE_COMPLEX)
6736       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6737 #else
6738       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6739 #endif
6740     }
6741     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6742   }
6743   PetscFunctionReturn(0);
6744 }
6745 
6746 /*
6747   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6748 
6749   Input Parameters:
6750 + section - The section for this data layout
6751 . islocal - Is the section (and thus indices being requested) local or global?
6752 . point   - The point contributing dofs with these indices
6753 . off     - The global offset of this point
6754 . loff    - The local offset of each field
6755 . setBC   - The flag determining whether to include indices of boundary values
6756 . perm    - A permutation of the dofs on this point, or NULL
6757 - indperm - A permutation of the entire indices array, or NULL
6758 
6759   Output Parameter:
6760 . indices - Indices for dofs on this point
6761 
6762   Level: developer
6763 
6764   Note: The indices could be local or global, depending on the value of 'off'.
6765 */
6766 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6767 {
6768   PetscInt        dof;   /* The number of unknowns on this point */
6769   PetscInt        cdof;  /* The number of constraints on this point */
6770   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6771   PetscInt        cind = 0, k;
6772 
6773   PetscFunctionBegin;
6774   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6775   PetscCall(PetscSectionGetDof(section, point, &dof));
6776   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6777   if (!cdof || setBC) {
6778     for (k = 0; k < dof; ++k) {
6779       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6780       const PetscInt ind    = indperm ? indperm[preind] : preind;
6781 
6782       indices[ind] = off + k;
6783     }
6784   } else {
6785     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6786     for (k = 0; k < dof; ++k) {
6787       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6788       const PetscInt ind    = indperm ? indperm[preind] : preind;
6789 
6790       if ((cind < cdof) && (k == cdofs[cind])) {
6791         /* Insert check for returning constrained indices */
6792         indices[ind] = -(off + k + 1);
6793         ++cind;
6794       } else {
6795         indices[ind] = off + k - (islocal ? 0 : cind);
6796       }
6797     }
6798   }
6799   *loff += dof;
6800   PetscFunctionReturn(0);
6801 }
6802 
6803 /*
6804  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6805 
6806  Input Parameters:
6807 + section - a section (global or local)
6808 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6809 . point - point within section
6810 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6811 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6812 . setBC - identify constrained (boundary condition) points via involution.
6813 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6814 . permsoff - offset
6815 - indperm - index permutation
6816 
6817  Output Parameter:
6818 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6819 . indices - array to hold indices (as defined by section) of each dof associated with point
6820 
6821  Notes:
6822  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6823  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6824  in the local vector.
6825 
6826  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6827  significant).  It is invalid to call with a global section and setBC=true.
6828 
6829  Developer Note:
6830  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6831  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6832  offset could be obtained from the section instead of passing it explicitly as we do now.
6833 
6834  Example:
6835  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6836  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6837  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6838  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.
6839 
6840  Level: developer
6841 */
6842 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[])
6843 {
6844   PetscInt numFields, foff, f;
6845 
6846   PetscFunctionBegin;
6847   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6848   PetscCall(PetscSectionGetNumFields(section, &numFields));
6849   for (f = 0, foff = 0; f < numFields; ++f) {
6850     PetscInt        fdof, cfdof;
6851     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6852     PetscInt        cind = 0, b;
6853     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6854 
6855     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6856     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6857     if (!cfdof || setBC) {
6858       for (b = 0; b < fdof; ++b) {
6859         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6860         const PetscInt ind    = indperm ? indperm[preind] : preind;
6861 
6862         indices[ind] = off + foff + b;
6863       }
6864     } else {
6865       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6866       for (b = 0; b < fdof; ++b) {
6867         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6868         const PetscInt ind    = indperm ? indperm[preind] : preind;
6869 
6870         if ((cind < cfdof) && (b == fcdofs[cind])) {
6871           indices[ind] = -(off + foff + b + 1);
6872           ++cind;
6873         } else {
6874           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6875         }
6876       }
6877     }
6878     foff += (setBC || islocal ? fdof : (fdof - cfdof));
6879     foffs[f] += fdof;
6880   }
6881   PetscFunctionReturn(0);
6882 }
6883 
6884 /*
6885   This version believes the globalSection offsets for each field, rather than just the point offset
6886 
6887  . foffs - The offset into 'indices' for each field, since it is segregated by field
6888 
6889  Notes:
6890  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6891  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6892 */
6893 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
6894 {
6895   PetscInt numFields, foff, f;
6896 
6897   PetscFunctionBegin;
6898   PetscCall(PetscSectionGetNumFields(section, &numFields));
6899   for (f = 0; f < numFields; ++f) {
6900     PetscInt        fdof, cfdof;
6901     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6902     PetscInt        cind = 0, b;
6903     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6904 
6905     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6906     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6907     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6908     if (!cfdof) {
6909       for (b = 0; b < fdof; ++b) {
6910         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6911         const PetscInt ind    = indperm ? indperm[preind] : preind;
6912 
6913         indices[ind] = foff + b;
6914       }
6915     } else {
6916       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6917       for (b = 0; b < fdof; ++b) {
6918         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6919         const PetscInt ind    = indperm ? indperm[preind] : preind;
6920 
6921         if ((cind < cfdof) && (b == fcdofs[cind])) {
6922           indices[ind] = -(foff + b + 1);
6923           ++cind;
6924         } else {
6925           indices[ind] = foff + b - cind;
6926         }
6927       }
6928     }
6929     foffs[f] += fdof;
6930   }
6931   PetscFunctionReturn(0);
6932 }
6933 
6934 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)
6935 {
6936   Mat             cMat;
6937   PetscSection    aSec, cSec;
6938   IS              aIS;
6939   PetscInt        aStart = -1, aEnd = -1;
6940   const PetscInt *anchors;
6941   PetscInt        numFields, f, p, q, newP = 0;
6942   PetscInt        newNumPoints = 0, newNumIndices = 0;
6943   PetscInt       *newPoints, *indices, *newIndices;
6944   PetscInt        maxAnchor, maxDof;
6945   PetscInt        newOffsets[32];
6946   PetscInt       *pointMatOffsets[32];
6947   PetscInt       *newPointOffsets[32];
6948   PetscScalar    *pointMat[32];
6949   PetscScalar    *newValues      = NULL, *tmpValues;
6950   PetscBool       anyConstrained = PETSC_FALSE;
6951 
6952   PetscFunctionBegin;
6953   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6954   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6955   PetscCall(PetscSectionGetNumFields(section, &numFields));
6956 
6957   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
6958   /* if there are point-to-point constraints */
6959   if (aSec) {
6960     PetscCall(PetscArrayzero(newOffsets, 32));
6961     PetscCall(ISGetIndices(aIS, &anchors));
6962     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
6963     /* figure out how many points are going to be in the new element matrix
6964      * (we allow double counting, because it's all just going to be summed
6965      * into the global matrix anyway) */
6966     for (p = 0; p < 2 * numPoints; p += 2) {
6967       PetscInt b    = points[p];
6968       PetscInt bDof = 0, bSecDof;
6969 
6970       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6971       if (!bSecDof) continue;
6972       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6973       if (bDof) {
6974         /* this point is constrained */
6975         /* it is going to be replaced by its anchors */
6976         PetscInt bOff, q;
6977 
6978         anyConstrained = PETSC_TRUE;
6979         newNumPoints += bDof;
6980         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6981         for (q = 0; q < bDof; q++) {
6982           PetscInt a = anchors[bOff + q];
6983           PetscInt aDof;
6984 
6985           PetscCall(PetscSectionGetDof(section, a, &aDof));
6986           newNumIndices += aDof;
6987           for (f = 0; f < numFields; ++f) {
6988             PetscInt fDof;
6989 
6990             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6991             newOffsets[f + 1] += fDof;
6992           }
6993         }
6994       } else {
6995         /* this point is not constrained */
6996         newNumPoints++;
6997         newNumIndices += bSecDof;
6998         for (f = 0; f < numFields; ++f) {
6999           PetscInt fDof;
7000 
7001           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7002           newOffsets[f + 1] += fDof;
7003         }
7004       }
7005     }
7006   }
7007   if (!anyConstrained) {
7008     if (outNumPoints) *outNumPoints = 0;
7009     if (outNumIndices) *outNumIndices = 0;
7010     if (outPoints) *outPoints = NULL;
7011     if (outValues) *outValues = NULL;
7012     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7013     PetscFunctionReturn(0);
7014   }
7015 
7016   if (outNumPoints) *outNumPoints = newNumPoints;
7017   if (outNumIndices) *outNumIndices = newNumIndices;
7018 
7019   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7020 
7021   if (!outPoints && !outValues) {
7022     if (offsets) {
7023       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7024     }
7025     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7026     PetscFunctionReturn(0);
7027   }
7028 
7029   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7030 
7031   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7032 
7033   /* workspaces */
7034   if (numFields) {
7035     for (f = 0; f < numFields; f++) {
7036       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7037       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7038     }
7039   } else {
7040     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7041     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7042   }
7043 
7044   /* get workspaces for the point-to-point matrices */
7045   if (numFields) {
7046     PetscInt totalOffset, totalMatOffset;
7047 
7048     for (p = 0; p < numPoints; p++) {
7049       PetscInt b    = points[2 * p];
7050       PetscInt bDof = 0, bSecDof;
7051 
7052       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7053       if (!bSecDof) {
7054         for (f = 0; f < numFields; f++) {
7055           newPointOffsets[f][p + 1] = 0;
7056           pointMatOffsets[f][p + 1] = 0;
7057         }
7058         continue;
7059       }
7060       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7061       if (bDof) {
7062         for (f = 0; f < numFields; f++) {
7063           PetscInt fDof, q, bOff, allFDof = 0;
7064 
7065           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7066           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7067           for (q = 0; q < bDof; q++) {
7068             PetscInt a = anchors[bOff + q];
7069             PetscInt aFDof;
7070 
7071             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7072             allFDof += aFDof;
7073           }
7074           newPointOffsets[f][p + 1] = allFDof;
7075           pointMatOffsets[f][p + 1] = fDof * allFDof;
7076         }
7077       } else {
7078         for (f = 0; f < numFields; f++) {
7079           PetscInt fDof;
7080 
7081           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7082           newPointOffsets[f][p + 1] = fDof;
7083           pointMatOffsets[f][p + 1] = 0;
7084         }
7085       }
7086     }
7087     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7088       newPointOffsets[f][0] = totalOffset;
7089       pointMatOffsets[f][0] = totalMatOffset;
7090       for (p = 0; p < numPoints; p++) {
7091         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7092         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7093       }
7094       totalOffset    = newPointOffsets[f][numPoints];
7095       totalMatOffset = pointMatOffsets[f][numPoints];
7096       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7097     }
7098   } else {
7099     for (p = 0; p < numPoints; p++) {
7100       PetscInt b    = points[2 * p];
7101       PetscInt bDof = 0, bSecDof;
7102 
7103       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7104       if (!bSecDof) {
7105         newPointOffsets[0][p + 1] = 0;
7106         pointMatOffsets[0][p + 1] = 0;
7107         continue;
7108       }
7109       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7110       if (bDof) {
7111         PetscInt bOff, q, allDof = 0;
7112 
7113         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7114         for (q = 0; q < bDof; q++) {
7115           PetscInt a = anchors[bOff + q], aDof;
7116 
7117           PetscCall(PetscSectionGetDof(section, a, &aDof));
7118           allDof += aDof;
7119         }
7120         newPointOffsets[0][p + 1] = allDof;
7121         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7122       } else {
7123         newPointOffsets[0][p + 1] = bSecDof;
7124         pointMatOffsets[0][p + 1] = 0;
7125       }
7126     }
7127     newPointOffsets[0][0] = 0;
7128     pointMatOffsets[0][0] = 0;
7129     for (p = 0; p < numPoints; p++) {
7130       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7131       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7132     }
7133     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7134   }
7135 
7136   /* output arrays */
7137   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7138 
7139   /* get the point-to-point matrices; construct newPoints */
7140   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7141   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7142   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7143   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7144   if (numFields) {
7145     for (p = 0, newP = 0; p < numPoints; p++) {
7146       PetscInt b    = points[2 * p];
7147       PetscInt o    = points[2 * p + 1];
7148       PetscInt bDof = 0, bSecDof;
7149 
7150       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7151       if (!bSecDof) continue;
7152       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7153       if (bDof) {
7154         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7155 
7156         fStart[0] = 0;
7157         fEnd[0]   = 0;
7158         for (f = 0; f < numFields; f++) {
7159           PetscInt fDof;
7160 
7161           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7162           fStart[f + 1] = fStart[f] + fDof;
7163           fEnd[f + 1]   = fStart[f + 1];
7164         }
7165         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7166         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7167 
7168         fAnchorStart[0] = 0;
7169         fAnchorEnd[0]   = 0;
7170         for (f = 0; f < numFields; f++) {
7171           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7172 
7173           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7174           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7175         }
7176         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7177         for (q = 0; q < bDof; q++) {
7178           PetscInt a = anchors[bOff + q], aOff;
7179 
7180           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7181           newPoints[2 * (newP + q)]     = a;
7182           newPoints[2 * (newP + q) + 1] = 0;
7183           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7184           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7185         }
7186         newP += bDof;
7187 
7188         if (outValues) {
7189           /* get the point-to-point submatrix */
7190           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]));
7191         }
7192       } else {
7193         newPoints[2 * newP]     = b;
7194         newPoints[2 * newP + 1] = o;
7195         newP++;
7196       }
7197     }
7198   } else {
7199     for (p = 0; p < numPoints; p++) {
7200       PetscInt b    = points[2 * p];
7201       PetscInt o    = points[2 * p + 1];
7202       PetscInt bDof = 0, bSecDof;
7203 
7204       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7205       if (!bSecDof) continue;
7206       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7207       if (bDof) {
7208         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7209 
7210         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7211         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7212 
7213         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7214         for (q = 0; q < bDof; q++) {
7215           PetscInt a = anchors[bOff + q], aOff;
7216 
7217           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7218 
7219           newPoints[2 * (newP + q)]     = a;
7220           newPoints[2 * (newP + q) + 1] = 0;
7221           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7222           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7223         }
7224         newP += bDof;
7225 
7226         /* get the point-to-point submatrix */
7227         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7228       } else {
7229         newPoints[2 * newP]     = b;
7230         newPoints[2 * newP + 1] = o;
7231         newP++;
7232       }
7233     }
7234   }
7235 
7236   if (outValues) {
7237     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7238     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7239     /* multiply constraints on the right */
7240     if (numFields) {
7241       for (f = 0; f < numFields; f++) {
7242         PetscInt oldOff = offsets[f];
7243 
7244         for (p = 0; p < numPoints; p++) {
7245           PetscInt cStart = newPointOffsets[f][p];
7246           PetscInt b      = points[2 * p];
7247           PetscInt c, r, k;
7248           PetscInt dof;
7249 
7250           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7251           if (!dof) continue;
7252           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7253             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7254             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7255 
7256             for (r = 0; r < numIndices; r++) {
7257               for (c = 0; c < nCols; c++) {
7258                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7259               }
7260             }
7261           } else {
7262             /* copy this column as is */
7263             for (r = 0; r < numIndices; r++) {
7264               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7265             }
7266           }
7267           oldOff += dof;
7268         }
7269       }
7270     } else {
7271       PetscInt oldOff = 0;
7272       for (p = 0; p < numPoints; p++) {
7273         PetscInt cStart = newPointOffsets[0][p];
7274         PetscInt b      = points[2 * p];
7275         PetscInt c, r, k;
7276         PetscInt dof;
7277 
7278         PetscCall(PetscSectionGetDof(section, b, &dof));
7279         if (!dof) continue;
7280         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7281           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7282           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7283 
7284           for (r = 0; r < numIndices; r++) {
7285             for (c = 0; c < nCols; c++) {
7286               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7287             }
7288           }
7289         } else {
7290           /* copy this column as is */
7291           for (r = 0; r < numIndices; r++) {
7292             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7293           }
7294         }
7295         oldOff += dof;
7296       }
7297     }
7298 
7299     if (multiplyLeft) {
7300       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7301       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7302       /* multiply constraints transpose on the left */
7303       if (numFields) {
7304         for (f = 0; f < numFields; f++) {
7305           PetscInt oldOff = offsets[f];
7306 
7307           for (p = 0; p < numPoints; p++) {
7308             PetscInt rStart = newPointOffsets[f][p];
7309             PetscInt b      = points[2 * p];
7310             PetscInt c, r, k;
7311             PetscInt dof;
7312 
7313             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7314             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7315               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7316               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7317 
7318               for (r = 0; r < nRows; r++) {
7319                 for (c = 0; c < newNumIndices; c++) {
7320                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7321                 }
7322               }
7323             } else {
7324               /* copy this row as is */
7325               for (r = 0; r < dof; r++) {
7326                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7327               }
7328             }
7329             oldOff += dof;
7330           }
7331         }
7332       } else {
7333         PetscInt oldOff = 0;
7334 
7335         for (p = 0; p < numPoints; p++) {
7336           PetscInt rStart = newPointOffsets[0][p];
7337           PetscInt b      = points[2 * p];
7338           PetscInt c, r, k;
7339           PetscInt dof;
7340 
7341           PetscCall(PetscSectionGetDof(section, b, &dof));
7342           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7343             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7344             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7345 
7346             for (r = 0; r < nRows; r++) {
7347               for (c = 0; c < newNumIndices; c++) {
7348                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7349               }
7350             }
7351           } else {
7352             /* copy this row as is */
7353             for (r = 0; r < dof; r++) {
7354               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7355             }
7356           }
7357           oldOff += dof;
7358         }
7359       }
7360 
7361       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7362     } else {
7363       newValues = tmpValues;
7364     }
7365   }
7366 
7367   /* clean up */
7368   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7369   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7370 
7371   if (numFields) {
7372     for (f = 0; f < numFields; f++) {
7373       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7374       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7375       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7376     }
7377   } else {
7378     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7379     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7380     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7381   }
7382   PetscCall(ISRestoreIndices(aIS, &anchors));
7383 
7384   /* output */
7385   if (outPoints) {
7386     *outPoints = newPoints;
7387   } else {
7388     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7389   }
7390   if (outValues) *outValues = newValues;
7391   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7392   PetscFunctionReturn(0);
7393 }
7394 
7395 /*@C
7396   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7397 
7398   Not collective
7399 
7400   Input Parameters:
7401 + dm         - The `DM`
7402 . section    - The `PetscSection` describing the points (a local section)
7403 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7404 . point      - The point defining the closure
7405 - useClPerm  - Use the closure point permutation if available
7406 
7407   Output Parameters:
7408 + numIndices - The number of dof indices in the closure of point with the input sections
7409 . indices    - The dof indices
7410 . outOffsets - Array to write the field offsets into, or NULL
7411 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7412 
7413   Level: advanced
7414 
7415   Notes:
7416   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7417 
7418   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7419   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7420   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7421   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7422   indices (with the above semantics) are implied.
7423 
7424 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7425           `PetscSection`, `DMGetGlobalSection()`
7426 @*/
7427 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7428 {
7429   /* Closure ordering */
7430   PetscSection    clSection;
7431   IS              clPoints;
7432   const PetscInt *clp;
7433   PetscInt       *points;
7434   const PetscInt *clperm = NULL;
7435   /* Dof permutation and sign flips */
7436   const PetscInt    **perms[32] = {NULL};
7437   const PetscScalar **flips[32] = {NULL};
7438   PetscScalar        *valCopy   = NULL;
7439   /* Hanging node constraints */
7440   PetscInt    *pointsC = NULL;
7441   PetscScalar *valuesC = NULL;
7442   PetscInt     NclC, NiC;
7443 
7444   PetscInt *idx;
7445   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7446   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7447 
7448   PetscFunctionBeginHot;
7449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7450   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7451   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7452   if (numIndices) PetscValidIntPointer(numIndices, 6);
7453   if (indices) PetscValidPointer(indices, 7);
7454   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7455   if (values) PetscValidPointer(values, 9);
7456   PetscCall(PetscSectionGetNumFields(section, &Nf));
7457   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7458   PetscCall(PetscArrayzero(offsets, 32));
7459   /* 1) Get points in closure */
7460   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7461   if (useClPerm) {
7462     PetscInt depth, clsize;
7463     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7464     for (clsize = 0, p = 0; p < Ncl; p++) {
7465       PetscInt dof;
7466       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7467       clsize += dof;
7468     }
7469     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7470   }
7471   /* 2) Get number of indices on these points and field offsets from section */
7472   for (p = 0; p < Ncl * 2; p += 2) {
7473     PetscInt dof, fdof;
7474 
7475     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7476     for (f = 0; f < Nf; ++f) {
7477       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7478       offsets[f + 1] += fdof;
7479     }
7480     Ni += dof;
7481   }
7482   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7483   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7484   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7485   for (f = 0; f < PetscMax(1, Nf); ++f) {
7486     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7487     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7488     /* may need to apply sign changes to the element matrix */
7489     if (values && flips[f]) {
7490       PetscInt foffset = offsets[f];
7491 
7492       for (p = 0; p < Ncl; ++p) {
7493         PetscInt           pnt  = points[2 * p], fdof;
7494         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7495 
7496         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7497         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7498         if (flip) {
7499           PetscInt i, j, k;
7500 
7501           if (!valCopy) {
7502             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7503             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7504             *values = valCopy;
7505           }
7506           for (i = 0; i < fdof; ++i) {
7507             PetscScalar fval = flip[i];
7508 
7509             for (k = 0; k < Ni; ++k) {
7510               valCopy[Ni * (foffset + i) + k] *= fval;
7511               valCopy[Ni * k + (foffset + i)] *= fval;
7512             }
7513           }
7514         }
7515         foffset += fdof;
7516       }
7517     }
7518   }
7519   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7520   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7521   if (NclC) {
7522     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7523     for (f = 0; f < PetscMax(1, Nf); ++f) {
7524       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7525       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7526     }
7527     for (f = 0; f < PetscMax(1, Nf); ++f) {
7528       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7529       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7530     }
7531     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7532     Ncl    = NclC;
7533     Ni     = NiC;
7534     points = pointsC;
7535     if (values) *values = valuesC;
7536   }
7537   /* 5) Calculate indices */
7538   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7539   if (Nf) {
7540     PetscInt  idxOff;
7541     PetscBool useFieldOffsets;
7542 
7543     if (outOffsets) {
7544       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7545     }
7546     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7547     if (useFieldOffsets) {
7548       for (p = 0; p < Ncl; ++p) {
7549         const PetscInt pnt = points[p * 2];
7550 
7551         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7552       }
7553     } else {
7554       for (p = 0; p < Ncl; ++p) {
7555         const PetscInt pnt = points[p * 2];
7556 
7557         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7558         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7559          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7560          * global section. */
7561         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7562       }
7563     }
7564   } else {
7565     PetscInt off = 0, idxOff;
7566 
7567     for (p = 0; p < Ncl; ++p) {
7568       const PetscInt  pnt  = points[p * 2];
7569       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7570 
7571       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7572       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7573        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7574       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7575     }
7576   }
7577   /* 6) Cleanup */
7578   for (f = 0; f < PetscMax(1, Nf); ++f) {
7579     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7580     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7581   }
7582   if (NclC) {
7583     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7584   } else {
7585     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7586   }
7587 
7588   if (numIndices) *numIndices = Ni;
7589   if (indices) *indices = idx;
7590   PetscFunctionReturn(0);
7591 }
7592 
7593 /*@C
7594   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7595 
7596   Not collective
7597 
7598   Input Parameters:
7599 + dm         - The `DM`
7600 . section    - The `PetscSection` describing the points (a local section)
7601 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7602 . point      - The point defining the closure
7603 - useClPerm  - Use the closure point permutation if available
7604 
7605   Output Parameters:
7606 + numIndices - The number of dof indices in the closure of point with the input sections
7607 . indices    - The dof indices
7608 . outOffsets - Array to write the field offsets into, or NULL
7609 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7610 
7611   Level: advanced
7612 
7613   Notes:
7614   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7615 
7616   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7617   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7618   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7619   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7620   indices (with the above semantics) are implied.
7621 
7622 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7623 @*/
7624 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7625 {
7626   PetscFunctionBegin;
7627   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7628   PetscValidPointer(indices, 7);
7629   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7630   PetscFunctionReturn(0);
7631 }
7632 
7633 /*@C
7634   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7635 
7636   Not collective
7637 
7638   Input Parameters:
7639 + dm - The `DM`
7640 . section - The section describing the layout in v, or NULL to use the default section
7641 . globalSection - The section describing the layout in v, or NULL to use the default global section
7642 . A - The matrix
7643 . point - The point in the `DM`
7644 . values - The array of values
7645 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7646 
7647   Level: intermediate
7648 
7649 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7650 @*/
7651 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7652 {
7653   DM_Plex           *mesh = (DM_Plex *)dm->data;
7654   PetscInt          *indices;
7655   PetscInt           numIndices;
7656   const PetscScalar *valuesOrig = values;
7657   PetscErrorCode     ierr;
7658 
7659   PetscFunctionBegin;
7660   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7661   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7662   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7663   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7664   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7665   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7666 
7667   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7668 
7669   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7670   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7671   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7672   if (ierr) {
7673     PetscMPIInt rank;
7674 
7675     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7676     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7677     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7678     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7679     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7680     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7681   }
7682   if (mesh->printFEM > 1) {
7683     PetscInt i;
7684     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7685     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7686     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7687   }
7688 
7689   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7690   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7691   PetscFunctionReturn(0);
7692 }
7693 
7694 /*@C
7695   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7696 
7697   Not collective
7698 
7699   Input Parameters:
7700 + dmRow - The `DM` for the row fields
7701 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7702 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7703 . dmCol - The `DM` for the column fields
7704 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7705 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7706 . A - The matrix
7707 . point - The point in the `DM`
7708 . values - The array of values
7709 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7710 
7711   Level: intermediate
7712 
7713 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7714 @*/
7715 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7716 {
7717   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7718   PetscInt          *indicesRow, *indicesCol;
7719   PetscInt           numIndicesRow, numIndicesCol;
7720   const PetscScalar *valuesOrig = values;
7721   PetscErrorCode     ierr;
7722 
7723   PetscFunctionBegin;
7724   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7725   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7726   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7727   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7728   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7729   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7730   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7731   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7732   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7733   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7734   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7735 
7736   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7737   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7738 
7739   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7740   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7741   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7742   if (ierr) {
7743     PetscMPIInt rank;
7744 
7745     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7746     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7747     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7748     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7749     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7750     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7751   }
7752 
7753   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7754   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7755   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7756   PetscFunctionReturn(0);
7757 }
7758 
7759 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7760 {
7761   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7762   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7763   PetscInt       *cpoints = NULL;
7764   PetscInt       *findices, *cindices;
7765   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7766   PetscInt        foffsets[32], coffsets[32];
7767   DMPolytopeType  ct;
7768   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7769   PetscErrorCode  ierr;
7770 
7771   PetscFunctionBegin;
7772   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7773   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7774   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7775   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7776   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7777   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7778   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7779   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7780   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7781   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7782   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7783   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7784   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7785   PetscCall(PetscArrayzero(foffsets, 32));
7786   PetscCall(PetscArrayzero(coffsets, 32));
7787   /* Column indices */
7788   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7789   maxFPoints = numCPoints;
7790   /* Compress out points not in the section */
7791   /*   TODO: Squeeze out points with 0 dof as well */
7792   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7793   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7794     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7795       cpoints[q * 2]     = cpoints[p];
7796       cpoints[q * 2 + 1] = cpoints[p + 1];
7797       ++q;
7798     }
7799   }
7800   numCPoints = q;
7801   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7802     PetscInt fdof;
7803 
7804     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7805     if (!dof) continue;
7806     for (f = 0; f < numFields; ++f) {
7807       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7808       coffsets[f + 1] += fdof;
7809     }
7810     numCIndices += dof;
7811   }
7812   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7813   /* Row indices */
7814   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7815   {
7816     DMPlexTransform tr;
7817     DMPolytopeType *rct;
7818     PetscInt       *rsize, *rcone, *rornt, Nt;
7819 
7820     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7821     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7822     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7823     numSubcells = rsize[Nt - 1];
7824     PetscCall(DMPlexTransformDestroy(&tr));
7825   }
7826   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7827   for (r = 0, q = 0; r < numSubcells; ++r) {
7828     /* TODO Map from coarse to fine cells */
7829     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7830     /* Compress out points not in the section */
7831     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7832     for (p = 0; p < numFPoints * 2; p += 2) {
7833       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7834         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7835         if (!dof) continue;
7836         for (s = 0; s < q; ++s)
7837           if (fpoints[p] == ftotpoints[s * 2]) break;
7838         if (s < q) continue;
7839         ftotpoints[q * 2]     = fpoints[p];
7840         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7841         ++q;
7842       }
7843     }
7844     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7845   }
7846   numFPoints = q;
7847   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7848     PetscInt fdof;
7849 
7850     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7851     if (!dof) continue;
7852     for (f = 0; f < numFields; ++f) {
7853       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7854       foffsets[f + 1] += fdof;
7855     }
7856     numFIndices += dof;
7857   }
7858   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7859 
7860   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7861   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7862   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7863   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7864   if (numFields) {
7865     const PetscInt **permsF[32] = {NULL};
7866     const PetscInt **permsC[32] = {NULL};
7867 
7868     for (f = 0; f < numFields; f++) {
7869       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7870       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7871     }
7872     for (p = 0; p < numFPoints; p++) {
7873       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7874       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7875     }
7876     for (p = 0; p < numCPoints; p++) {
7877       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7878       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7879     }
7880     for (f = 0; f < numFields; f++) {
7881       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7882       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7883     }
7884   } else {
7885     const PetscInt **permsF = NULL;
7886     const PetscInt **permsC = NULL;
7887 
7888     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7889     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7890     for (p = 0, off = 0; p < numFPoints; p++) {
7891       const PetscInt *perm = permsF ? permsF[p] : NULL;
7892 
7893       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7894       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7895     }
7896     for (p = 0, off = 0; p < numCPoints; p++) {
7897       const PetscInt *perm = permsC ? permsC[p] : NULL;
7898 
7899       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7900       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7901     }
7902     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7903     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7904   }
7905   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7906   /* TODO: flips */
7907   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7908   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7909   if (ierr) {
7910     PetscMPIInt rank;
7911 
7912     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7913     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7914     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7915     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7916     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7917   }
7918   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
7919   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7920   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7921   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7922   PetscFunctionReturn(0);
7923 }
7924 
7925 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
7926 {
7927   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7928   PetscInt       *cpoints = NULL;
7929   PetscInt        foffsets[32], coffsets[32];
7930   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7931   DMPolytopeType  ct;
7932   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7933 
7934   PetscFunctionBegin;
7935   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7936   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7937   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7938   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7939   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7940   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7941   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7942   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7943   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7944   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7945   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7946   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7947   PetscCall(PetscArrayzero(foffsets, 32));
7948   PetscCall(PetscArrayzero(coffsets, 32));
7949   /* Column indices */
7950   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7951   maxFPoints = numCPoints;
7952   /* Compress out points not in the section */
7953   /*   TODO: Squeeze out points with 0 dof as well */
7954   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7955   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7956     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7957       cpoints[q * 2]     = cpoints[p];
7958       cpoints[q * 2 + 1] = cpoints[p + 1];
7959       ++q;
7960     }
7961   }
7962   numCPoints = q;
7963   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7964     PetscInt fdof;
7965 
7966     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7967     if (!dof) continue;
7968     for (f = 0; f < numFields; ++f) {
7969       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7970       coffsets[f + 1] += fdof;
7971     }
7972     numCIndices += dof;
7973   }
7974   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7975   /* Row indices */
7976   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7977   {
7978     DMPlexTransform tr;
7979     DMPolytopeType *rct;
7980     PetscInt       *rsize, *rcone, *rornt, Nt;
7981 
7982     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7983     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7984     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7985     numSubcells = rsize[Nt - 1];
7986     PetscCall(DMPlexTransformDestroy(&tr));
7987   }
7988   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7989   for (r = 0, q = 0; r < numSubcells; ++r) {
7990     /* TODO Map from coarse to fine cells */
7991     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7992     /* Compress out points not in the section */
7993     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7994     for (p = 0; p < numFPoints * 2; p += 2) {
7995       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7996         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7997         if (!dof) continue;
7998         for (s = 0; s < q; ++s)
7999           if (fpoints[p] == ftotpoints[s * 2]) break;
8000         if (s < q) continue;
8001         ftotpoints[q * 2]     = fpoints[p];
8002         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8003         ++q;
8004       }
8005     }
8006     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8007   }
8008   numFPoints = q;
8009   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8010     PetscInt fdof;
8011 
8012     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8013     if (!dof) continue;
8014     for (f = 0; f < numFields; ++f) {
8015       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8016       foffsets[f + 1] += fdof;
8017     }
8018     numFIndices += dof;
8019   }
8020   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8021 
8022   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8023   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8024   if (numFields) {
8025     const PetscInt **permsF[32] = {NULL};
8026     const PetscInt **permsC[32] = {NULL};
8027 
8028     for (f = 0; f < numFields; f++) {
8029       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8030       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8031     }
8032     for (p = 0; p < numFPoints; p++) {
8033       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8034       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8035     }
8036     for (p = 0; p < numCPoints; p++) {
8037       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8038       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8039     }
8040     for (f = 0; f < numFields; f++) {
8041       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8042       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8043     }
8044   } else {
8045     const PetscInt **permsF = NULL;
8046     const PetscInt **permsC = NULL;
8047 
8048     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8049     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8050     for (p = 0, off = 0; p < numFPoints; p++) {
8051       const PetscInt *perm = permsF ? permsF[p] : NULL;
8052 
8053       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8054       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8055     }
8056     for (p = 0, off = 0; p < numCPoints; p++) {
8057       const PetscInt *perm = permsC ? permsC[p] : NULL;
8058 
8059       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8060       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8061     }
8062     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8063     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8064   }
8065   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8066   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8067   PetscFunctionReturn(0);
8068 }
8069 
8070 /*@C
8071   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8072 
8073   Input Parameter:
8074 . dm   - The `DMPLEX` object
8075 
8076   Output Parameter:
8077 . cellHeight - The height of a cell
8078 
8079   Level: developer
8080 
8081 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`,  `DMPlexSetVTKCellHeight()`
8082 @*/
8083 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8084 {
8085   DM_Plex *mesh = (DM_Plex *)dm->data;
8086 
8087   PetscFunctionBegin;
8088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8089   PetscValidIntPointer(cellHeight, 2);
8090   *cellHeight = mesh->vtkCellHeight;
8091   PetscFunctionReturn(0);
8092 }
8093 
8094 /*@C
8095   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8096 
8097   Input Parameters:
8098 + dm   - The `DMPLEX` object
8099 - cellHeight - The height of a cell
8100 
8101   Level: developer
8102 
8103 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8104 @*/
8105 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8106 {
8107   DM_Plex *mesh = (DM_Plex *)dm->data;
8108 
8109   PetscFunctionBegin;
8110   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8111   mesh->vtkCellHeight = cellHeight;
8112   PetscFunctionReturn(0);
8113 }
8114 
8115 /*@
8116   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8117 
8118   Input Parameter:
8119 . dm - The `DMPLEX` object
8120 
8121   Output Parameters:
8122 + gcStart - The first ghost cell, or NULL
8123 - gcEnd   - The upper bound on ghost cells, or NULL
8124 
8125   Level: advanced
8126 
8127 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8128 @*/
8129 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8130 {
8131   DMLabel ctLabel;
8132 
8133   PetscFunctionBegin;
8134   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8135   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8136   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8137   // Reset label for fast lookup
8138   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8139   PetscFunctionReturn(0);
8140 }
8141 
8142 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8143 {
8144   PetscSection section, globalSection;
8145   PetscInt    *numbers, p;
8146 
8147   PetscFunctionBegin;
8148   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8149   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8150   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8151   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8152   PetscCall(PetscSectionSetUp(section));
8153   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8154   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8155   for (p = pStart; p < pEnd; ++p) {
8156     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8157     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8158     else numbers[p - pStart] += shift;
8159   }
8160   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8161   if (globalSize) {
8162     PetscLayout layout;
8163     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8164     PetscCall(PetscLayoutGetSize(layout, globalSize));
8165     PetscCall(PetscLayoutDestroy(&layout));
8166   }
8167   PetscCall(PetscSectionDestroy(&section));
8168   PetscCall(PetscSectionDestroy(&globalSection));
8169   PetscFunctionReturn(0);
8170 }
8171 
8172 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8173 {
8174   PetscInt cellHeight, cStart, cEnd;
8175 
8176   PetscFunctionBegin;
8177   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8178   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8179   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8180   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8181   PetscFunctionReturn(0);
8182 }
8183 
8184 /*@
8185   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8186 
8187   Input Parameter:
8188 . dm   - The `DMPLEX` object
8189 
8190   Output Parameter:
8191 . globalCellNumbers - Global cell numbers for all cells on this process
8192 
8193   Level: developer
8194 
8195 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8196 @*/
8197 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8198 {
8199   DM_Plex *mesh = (DM_Plex *)dm->data;
8200 
8201   PetscFunctionBegin;
8202   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8203   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8204   *globalCellNumbers = mesh->globalCellNumbers;
8205   PetscFunctionReturn(0);
8206 }
8207 
8208 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8209 {
8210   PetscInt vStart, vEnd;
8211 
8212   PetscFunctionBegin;
8213   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8214   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8215   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8216   PetscFunctionReturn(0);
8217 }
8218 
8219 /*@
8220   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8221 
8222   Input Parameter:
8223 . dm   - The `DMPLEX` object
8224 
8225   Output Parameter:
8226 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8227 
8228   Level: developer
8229 
8230 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8231 @*/
8232 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8233 {
8234   DM_Plex *mesh = (DM_Plex *)dm->data;
8235 
8236   PetscFunctionBegin;
8237   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8238   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8239   *globalVertexNumbers = mesh->globalVertexNumbers;
8240   PetscFunctionReturn(0);
8241 }
8242 
8243 /*@
8244   DMPlexCreatePointNumbering - Create a global numbering for all points.
8245 
8246   Collective on dm
8247 
8248   Input Parameter:
8249 . dm   - The `DMPLEX` object
8250 
8251   Output Parameter:
8252 . globalPointNumbers - Global numbers for all points on this process
8253 
8254   Level: developer
8255 
8256   Notes:
8257   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8258   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8259   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8260   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8261 
8262   The partitioned mesh is
8263 ```
8264  (2)--0--(3)--1--(4)    (1)--0--(2)
8265 ```
8266   and its global numbering is
8267 ```
8268   (3)--0--(4)--1--(5)--2--(6)
8269 ```
8270   Then the global numbering is provided as
8271 ```
8272 [0] Number of indices in set 5
8273 [0] 0 0
8274 [0] 1 1
8275 [0] 2 3
8276 [0] 3 4
8277 [0] 4 -6
8278 [1] Number of indices in set 3
8279 [1] 0 2
8280 [1] 1 5
8281 [1] 2 6
8282 ```
8283 
8284 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8285 @*/
8286 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8287 {
8288   IS        nums[4];
8289   PetscInt  depths[4], gdepths[4], starts[4];
8290   PetscInt  depth, d, shift = 0;
8291   PetscBool empty = PETSC_FALSE;
8292 
8293   PetscFunctionBegin;
8294   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8295   PetscCall(DMPlexGetDepth(dm, &depth));
8296   // For unstratified meshes use dim instead of depth
8297   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8298   // If any stratum is empty, we must mark all empty
8299   for (d = 0; d <= depth; ++d) {
8300     PetscInt end;
8301 
8302     depths[d] = depth - d;
8303     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8304     if (!(starts[d] - end)) empty = PETSC_TRUE;
8305   }
8306   if (empty)
8307     for (d = 0; d <= depth; ++d) {
8308       depths[d] = -1;
8309       starts[d] = -1;
8310     }
8311   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8312   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8313   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]);
8314   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8315   for (d = 0; d <= depth; ++d) {
8316     PetscInt pStart, pEnd, gsize;
8317 
8318     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8319     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8320     shift += gsize;
8321   }
8322   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8323   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8324   PetscFunctionReturn(0);
8325 }
8326 
8327 /*@
8328   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8329 
8330   Input Parameter:
8331 . dm - The `DMPLEX` object
8332 
8333   Output Parameter:
8334 . ranks - The rank field
8335 
8336   Options Database Key:
8337 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8338 
8339   Level: intermediate
8340 
8341 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8342 @*/
8343 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8344 {
8345   DM             rdm;
8346   PetscFE        fe;
8347   PetscScalar   *r;
8348   PetscMPIInt    rank;
8349   DMPolytopeType ct;
8350   PetscInt       dim, cStart, cEnd, c;
8351   PetscBool      simplex;
8352 
8353   PetscFunctionBeginUser;
8354   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8355   PetscValidPointer(ranks, 2);
8356   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8357   PetscCall(DMClone(dm, &rdm));
8358   PetscCall(DMGetDimension(rdm, &dim));
8359   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8360   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8361   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8362   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8363   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8364   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8365   PetscCall(PetscFEDestroy(&fe));
8366   PetscCall(DMCreateDS(rdm));
8367   PetscCall(DMCreateGlobalVector(rdm, ranks));
8368   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8369   PetscCall(VecGetArray(*ranks, &r));
8370   for (c = cStart; c < cEnd; ++c) {
8371     PetscScalar *lr;
8372 
8373     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8374     if (lr) *lr = rank;
8375   }
8376   PetscCall(VecRestoreArray(*ranks, &r));
8377   PetscCall(DMDestroy(&rdm));
8378   PetscFunctionReturn(0);
8379 }
8380 
8381 /*@
8382   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8383 
8384   Input Parameters:
8385 + dm    - The DMPlex
8386 - label - The DMLabel
8387 
8388   Output Parameter:
8389 . val - The label value field
8390 
8391   Options Database Keys:
8392 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8393 
8394   Level: intermediate
8395 
8396 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8397 @*/
8398 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8399 {
8400   DM           rdm;
8401   PetscFE      fe;
8402   PetscScalar *v;
8403   PetscInt     dim, cStart, cEnd, c;
8404 
8405   PetscFunctionBeginUser;
8406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8407   PetscValidPointer(label, 2);
8408   PetscValidPointer(val, 3);
8409   PetscCall(DMClone(dm, &rdm));
8410   PetscCall(DMGetDimension(rdm, &dim));
8411   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8412   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8413   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8414   PetscCall(PetscFEDestroy(&fe));
8415   PetscCall(DMCreateDS(rdm));
8416   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8417   PetscCall(DMCreateGlobalVector(rdm, val));
8418   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8419   PetscCall(VecGetArray(*val, &v));
8420   for (c = cStart; c < cEnd; ++c) {
8421     PetscScalar *lv;
8422     PetscInt     cval;
8423 
8424     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8425     PetscCall(DMLabelGetValue(label, c, &cval));
8426     *lv = cval;
8427   }
8428   PetscCall(VecRestoreArray(*val, &v));
8429   PetscCall(DMDestroy(&rdm));
8430   PetscFunctionReturn(0);
8431 }
8432 
8433 /*@
8434   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8435 
8436   Input Parameter:
8437 . dm - The `DMPLEX` object
8438 
8439   Level: developer
8440 
8441   Notes:
8442   This is a useful diagnostic when creating meshes programmatically.
8443 
8444   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8445 
8446 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8447 @*/
8448 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8449 {
8450   PetscSection    coneSection, supportSection;
8451   const PetscInt *cone, *support;
8452   PetscInt        coneSize, c, supportSize, s;
8453   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8454   PetscBool       storagecheck = PETSC_TRUE;
8455 
8456   PetscFunctionBegin;
8457   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8458   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8459   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8460   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8461   /* Check that point p is found in the support of its cone points, and vice versa */
8462   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8463   for (p = pStart; p < pEnd; ++p) {
8464     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8465     PetscCall(DMPlexGetCone(dm, p, &cone));
8466     for (c = 0; c < coneSize; ++c) {
8467       PetscBool dup = PETSC_FALSE;
8468       PetscInt  d;
8469       for (d = c - 1; d >= 0; --d) {
8470         if (cone[c] == cone[d]) {
8471           dup = PETSC_TRUE;
8472           break;
8473         }
8474       }
8475       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8476       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8477       for (s = 0; s < supportSize; ++s) {
8478         if (support[s] == p) break;
8479       }
8480       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8481         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8482         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8483         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8484         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8485         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8486         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8487         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]);
8488         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8489       }
8490     }
8491     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8492     if (p != pp) {
8493       storagecheck = PETSC_FALSE;
8494       continue;
8495     }
8496     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8497     PetscCall(DMPlexGetSupport(dm, p, &support));
8498     for (s = 0; s < supportSize; ++s) {
8499       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8500       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8501       for (c = 0; c < coneSize; ++c) {
8502         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8503         if (cone[c] != pp) {
8504           c = 0;
8505           break;
8506         }
8507         if (cone[c] == p) break;
8508       }
8509       if (c >= coneSize) {
8510         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8511         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8512         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8513         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8514         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8515         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8516         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8517       }
8518     }
8519   }
8520   if (storagecheck) {
8521     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8522     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8523     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8524   }
8525   PetscFunctionReturn(0);
8526 }
8527 
8528 /*
8529   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.
8530 */
8531 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8532 {
8533   DMPolytopeType  cct;
8534   PetscInt        ptpoints[4];
8535   const PetscInt *cone, *ccone, *ptcone;
8536   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8537 
8538   PetscFunctionBegin;
8539   *unsplit = 0;
8540   switch (ct) {
8541   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8542     ptpoints[npt++] = c;
8543     break;
8544   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8545     PetscCall(DMPlexGetCone(dm, c, &cone));
8546     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8547     for (cp = 0; cp < coneSize; ++cp) {
8548       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8549       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8550     }
8551     break;
8552   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8553   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8554     PetscCall(DMPlexGetCone(dm, c, &cone));
8555     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8556     for (cp = 0; cp < coneSize; ++cp) {
8557       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8558       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8559       for (ccp = 0; ccp < cconeSize; ++ccp) {
8560         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8561         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8562           PetscInt p;
8563           for (p = 0; p < npt; ++p)
8564             if (ptpoints[p] == ccone[ccp]) break;
8565           if (p == npt) ptpoints[npt++] = ccone[ccp];
8566         }
8567       }
8568     }
8569     break;
8570   default:
8571     break;
8572   }
8573   for (pt = 0; pt < npt; ++pt) {
8574     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8575     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8576   }
8577   PetscFunctionReturn(0);
8578 }
8579 
8580 /*@
8581   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8582 
8583   Input Parameters:
8584 + dm - The `DMPLEX` object
8585 - cellHeight - Normally 0
8586 
8587   Level: developer
8588 
8589   Notes:
8590   This is a useful diagnostic when creating meshes programmatically.
8591   Currently applicable only to homogeneous simplex or tensor meshes.
8592 
8593   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8594 
8595 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8596 @*/
8597 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8598 {
8599   DMPlexInterpolatedFlag interp;
8600   DMPolytopeType         ct;
8601   PetscInt               vStart, vEnd, cStart, cEnd, c;
8602 
8603   PetscFunctionBegin;
8604   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8605   PetscCall(DMPlexIsInterpolated(dm, &interp));
8606   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8607   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8608   for (c = cStart; c < cEnd; ++c) {
8609     PetscInt *closure = NULL;
8610     PetscInt  coneSize, closureSize, cl, Nv = 0;
8611 
8612     PetscCall(DMPlexGetCellType(dm, c, &ct));
8613     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8614     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8615     if (interp == DMPLEX_INTERPOLATED_FULL) {
8616       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8617       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));
8618     }
8619     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8620     for (cl = 0; cl < closureSize * 2; cl += 2) {
8621       const PetscInt p = closure[cl];
8622       if ((p >= vStart) && (p < vEnd)) ++Nv;
8623     }
8624     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8625     /* Special Case: Tensor faces with identified vertices */
8626     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8627       PetscInt unsplit;
8628 
8629       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8630       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8631     }
8632     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));
8633   }
8634   PetscFunctionReturn(0);
8635 }
8636 
8637 /*@
8638   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8639 
8640   Collective on dm
8641 
8642   Input Parameters:
8643 + dm - The `DMPLEX` object
8644 - cellHeight - Normally 0
8645 
8646   Level: developer
8647 
8648   Notes:
8649   This is a useful diagnostic when creating meshes programmatically.
8650   This routine is only relevant for meshes that are fully interpolated across all ranks.
8651   It will error out if a partially interpolated mesh is given on some rank.
8652   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8653 
8654   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8655 
8656 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8657 @*/
8658 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8659 {
8660   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8661   DMPlexInterpolatedFlag interpEnum;
8662 
8663   PetscFunctionBegin;
8664   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8665   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8666   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8667   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8668     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8669     PetscFunctionReturn(0);
8670   }
8671 
8672   PetscCall(DMGetDimension(dm, &dim));
8673   PetscCall(DMPlexGetDepth(dm, &depth));
8674   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8675   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8676     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8677     for (c = cStart; c < cEnd; ++c) {
8678       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8679       const DMPolytopeType *faceTypes;
8680       DMPolytopeType        ct;
8681       PetscInt              numFaces, coneSize, f;
8682       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8683 
8684       PetscCall(DMPlexGetCellType(dm, c, &ct));
8685       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8686       if (unsplit) continue;
8687       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8688       PetscCall(DMPlexGetCone(dm, c, &cone));
8689       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8690       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8691       for (cl = 0; cl < closureSize * 2; cl += 2) {
8692         const PetscInt p = closure[cl];
8693         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8694       }
8695       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8696       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);
8697       for (f = 0; f < numFaces; ++f) {
8698         DMPolytopeType fct;
8699         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8700 
8701         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8702         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8703         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8704           const PetscInt p = fclosure[cl];
8705           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8706         }
8707         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]);
8708         for (v = 0; v < fnumCorners; ++v) {
8709           if (fclosure[v] != faces[fOff + v]) {
8710             PetscInt v1;
8711 
8712             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8713             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8714             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8715             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8716             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8717             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]);
8718           }
8719         }
8720         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8721         fOff += faceSizes[f];
8722       }
8723       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8724       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8725     }
8726   }
8727   PetscFunctionReturn(0);
8728 }
8729 
8730 /*@
8731   DMPlexCheckGeometry - Check the geometry of mesh cells
8732 
8733   Input Parameter:
8734 . dm - The `DMPLEX` object
8735 
8736   Level: developer
8737 
8738   Notes:
8739   This is a useful diagnostic when creating meshes programmatically.
8740 
8741   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8742 
8743 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8744 @*/
8745 PetscErrorCode DMPlexCheckGeometry(DM dm)
8746 {
8747   Vec       coordinates;
8748   PetscReal detJ, J[9], refVol = 1.0;
8749   PetscReal vol;
8750   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8751 
8752   PetscFunctionBegin;
8753   PetscCall(DMGetDimension(dm, &dim));
8754   PetscCall(DMGetCoordinateDim(dm, &dE));
8755   if (dim != dE) PetscFunctionReturn(0);
8756   PetscCall(DMPlexGetDepth(dm, &depth));
8757   for (d = 0; d < dim; ++d) refVol *= 2.0;
8758   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8759   /* Make sure local coordinates are created, because that step is collective */
8760   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8761   for (c = cStart; c < cEnd; ++c) {
8762     DMPolytopeType ct;
8763     PetscInt       unsplit;
8764     PetscBool      ignoreZeroVol = PETSC_FALSE;
8765 
8766     PetscCall(DMPlexGetCellType(dm, c, &ct));
8767     switch (ct) {
8768     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8769     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8770     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8771       ignoreZeroVol = PETSC_TRUE;
8772       break;
8773     default:
8774       break;
8775     }
8776     switch (ct) {
8777     case DM_POLYTOPE_TRI_PRISM:
8778     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8779     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8780     case DM_POLYTOPE_PYRAMID:
8781       continue;
8782     default:
8783       break;
8784     }
8785     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8786     if (unsplit) continue;
8787     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8788     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);
8789     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8790     /* This should work with periodicity since DG coordinates should be used */
8791     if (depth > 1) {
8792       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8793       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);
8794       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8795     }
8796   }
8797   PetscFunctionReturn(0);
8798 }
8799 
8800 /*@
8801   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8802 
8803   Collective on dm
8804 
8805   Input Parameters:
8806 + dm - The `DMPLEX` object
8807 . pointSF - The `PetscSF`, or NULL for `PointSF` attached to `DM`
8808 - allowExtraRoots - Flag to allow extra points not present in the `DM`
8809 
8810   Level: developer
8811 
8812   Notes:
8813   This is mainly intended for debugging/testing purposes.
8814 
8815   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8816 
8817   Extra roots can come from priodic cuts, where additional points appear on the boundary
8818 
8819 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
8820 @*/
8821 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
8822 {
8823   PetscInt           l, nleaves, nroots, overlap;
8824   const PetscInt    *locals;
8825   const PetscSFNode *remotes;
8826   PetscBool          distributed;
8827   MPI_Comm           comm;
8828   PetscMPIInt        rank;
8829 
8830   PetscFunctionBegin;
8831   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8832   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8833   else pointSF = dm->sf;
8834   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8835   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8836   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8837   {
8838     PetscMPIInt mpiFlag;
8839 
8840     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8841     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8842   }
8843   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8844   PetscCall(DMPlexIsDistributed(dm, &distributed));
8845   if (!distributed) {
8846     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);
8847     PetscFunctionReturn(0);
8848   }
8849   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);
8850   PetscCall(DMPlexGetOverlap(dm, &overlap));
8851 
8852   /* Check SF graph is compatible with DMPlex chart */
8853   {
8854     PetscInt pStart, pEnd, maxLeaf;
8855 
8856     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8857     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8858     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
8859     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8860   }
8861 
8862   /* Check Point SF has no local points referenced */
8863   for (l = 0; l < nleaves; l++) {
8864     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);
8865   }
8866 
8867   /* Check there are no cells in interface */
8868   if (!overlap) {
8869     PetscInt cellHeight, cStart, cEnd;
8870 
8871     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8872     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8873     for (l = 0; l < nleaves; ++l) {
8874       const PetscInt point = locals ? locals[l] : l;
8875 
8876       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8877     }
8878   }
8879 
8880   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8881   {
8882     const PetscInt *rootdegree;
8883 
8884     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8885     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8886     for (l = 0; l < nleaves; ++l) {
8887       const PetscInt  point = locals ? locals[l] : l;
8888       const PetscInt *cone;
8889       PetscInt        coneSize, c, idx;
8890 
8891       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8892       PetscCall(DMPlexGetCone(dm, point, &cone));
8893       for (c = 0; c < coneSize; ++c) {
8894         if (!rootdegree[cone[c]]) {
8895           if (locals) {
8896             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8897           } else {
8898             idx = (cone[c] < nleaves) ? cone[c] : -1;
8899           }
8900           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8901         }
8902       }
8903     }
8904   }
8905   PetscFunctionReturn(0);
8906 }
8907 
8908 /*@
8909   DMPlexCheck - Perform various checks of Plex sanity
8910 
8911   Input Parameter:
8912 . dm - The `DMPLEX` object
8913 
8914   Level: developer
8915 
8916   Notes:
8917   This is a useful diagnostic when creating meshes programmatically.
8918 
8919   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8920 
8921   Currently does not include DMPlexCheckCellShape().
8922 
8923 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, DMCreate(), DMSetFromOptions()
8924 @*/
8925 PetscErrorCode DMPlexCheck(DM dm)
8926 {
8927   PetscInt cellHeight;
8928 
8929   PetscFunctionBegin;
8930   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8931   PetscCall(DMPlexCheckSymmetry(dm));
8932   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8933   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8934   PetscCall(DMPlexCheckGeometry(dm));
8935   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
8936   PetscCall(DMPlexCheckInterfaceCones(dm));
8937   PetscFunctionReturn(0);
8938 }
8939 
8940 typedef struct cell_stats {
8941   PetscReal min, max, sum, squaresum;
8942   PetscInt  count;
8943 } cell_stats_t;
8944 
8945 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
8946 {
8947   PetscInt i, N = *len;
8948 
8949   for (i = 0; i < N; i++) {
8950     cell_stats_t *A = (cell_stats_t *)a;
8951     cell_stats_t *B = (cell_stats_t *)b;
8952 
8953     B->min = PetscMin(A->min, B->min);
8954     B->max = PetscMax(A->max, B->max);
8955     B->sum += A->sum;
8956     B->squaresum += A->squaresum;
8957     B->count += A->count;
8958   }
8959 }
8960 
8961 /*@
8962   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8963 
8964   Collective on dm
8965 
8966   Input Parameters:
8967 + dm        - The `DMPLEX` object
8968 . output    - If true, statistics will be displayed on stdout
8969 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
8970 
8971   Level: developer
8972 
8973   Notes:
8974   This is mainly intended for debugging/testing purposes.
8975 
8976   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8977 
8978 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8979 @*/
8980 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
8981 {
8982   DM           dmCoarse;
8983   cell_stats_t stats, globalStats;
8984   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
8985   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8986   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8987   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8988   PetscMPIInt  rank, size;
8989 
8990   PetscFunctionBegin;
8991   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8992   stats.min = PETSC_MAX_REAL;
8993   stats.max = PETSC_MIN_REAL;
8994   stats.sum = stats.squaresum = 0.;
8995   stats.count                 = 0;
8996 
8997   PetscCallMPI(MPI_Comm_size(comm, &size));
8998   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8999   PetscCall(DMGetCoordinateDim(dm, &cdim));
9000   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9001   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9002   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9003   for (c = cStart; c < cEnd; c++) {
9004     PetscInt  i;
9005     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9006 
9007     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9008     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9009     for (i = 0; i < PetscSqr(cdim); ++i) {
9010       frobJ += J[i] * J[i];
9011       frobInvJ += invJ[i] * invJ[i];
9012     }
9013     cond2 = frobJ * frobInvJ;
9014     cond  = PetscSqrtReal(cond2);
9015 
9016     stats.min = PetscMin(stats.min, cond);
9017     stats.max = PetscMax(stats.max, cond);
9018     stats.sum += cond;
9019     stats.squaresum += cond2;
9020     stats.count++;
9021     if (output && cond > limit) {
9022       PetscSection coordSection;
9023       Vec          coordsLocal;
9024       PetscScalar *coords = NULL;
9025       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9026 
9027       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9028       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9029       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9030       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9031       for (i = 0; i < Nv / cdim; ++i) {
9032         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9033         for (d = 0; d < cdim; ++d) {
9034           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9035           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9036         }
9037         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9038       }
9039       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9040       for (cl = 0; cl < clSize * 2; cl += 2) {
9041         const PetscInt edge = closure[cl];
9042 
9043         if ((edge >= eStart) && (edge < eEnd)) {
9044           PetscReal len;
9045 
9046           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9047           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9048         }
9049       }
9050       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9051       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9052     }
9053   }
9054   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9055 
9056   if (size > 1) {
9057     PetscMPIInt  blockLengths[2] = {4, 1};
9058     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9059     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9060     MPI_Op       statReduce;
9061 
9062     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9063     PetscCallMPI(MPI_Type_commit(&statType));
9064     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9065     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9066     PetscCallMPI(MPI_Op_free(&statReduce));
9067     PetscCallMPI(MPI_Type_free(&statType));
9068   } else {
9069     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9070   }
9071   if (rank == 0) {
9072     count = globalStats.count;
9073     min   = globalStats.min;
9074     max   = globalStats.max;
9075     mean  = globalStats.sum / globalStats.count;
9076     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9077   }
9078 
9079   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));
9080   PetscCall(PetscFree2(J, invJ));
9081 
9082   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9083   if (dmCoarse) {
9084     PetscBool isplex;
9085 
9086     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9087     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9088   }
9089   PetscFunctionReturn(0);
9090 }
9091 
9092 /*@
9093   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9094   orthogonal quality below given tolerance.
9095 
9096   Collective on dm
9097 
9098   Input Parameters:
9099 + dm   - The `DMPLEX` object
9100 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9101 - atol - [0, 1] Absolute tolerance for tagging cells.
9102 
9103   Output Parameters:
9104 + OrthQual      - Vec containing orthogonal quality per cell
9105 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9106 
9107   Options Database Keys:
9108 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9109 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9110 
9111   Level: intermediate
9112 
9113   Notes:
9114   Orthogonal quality is given by the following formula:
9115 
9116   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9117 
9118   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
9119   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9120   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9121   calculating the cosine of the angle between these vectors.
9122 
9123   Orthogonal quality ranges from 1 (best) to 0 (worst).
9124 
9125   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9126   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9127 
9128   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9129 
9130 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9131 @*/
9132 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9133 {
9134   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9135   PetscInt              *idx;
9136   PetscScalar           *oqVals;
9137   const PetscScalar     *cellGeomArr, *faceGeomArr;
9138   PetscReal             *ci, *fi, *Ai;
9139   MPI_Comm               comm;
9140   Vec                    cellgeom, facegeom;
9141   DM                     dmFace, dmCell;
9142   IS                     glob;
9143   ISLocalToGlobalMapping ltog;
9144   PetscViewer            vwr;
9145 
9146   PetscFunctionBegin;
9147   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9148   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9149   PetscValidPointer(OrthQual, 4);
9150   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9151   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9152   PetscCall(DMGetDimension(dm, &nc));
9153   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9154   {
9155     DMPlexInterpolatedFlag interpFlag;
9156 
9157     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9158     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9159       PetscMPIInt rank;
9160 
9161       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9162       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9163     }
9164   }
9165   if (OrthQualLabel) {
9166     PetscValidPointer(OrthQualLabel, 5);
9167     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9168     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9169   } else {
9170     *OrthQualLabel = NULL;
9171   }
9172   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9173   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9174   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9175   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9176   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9177   PetscCall(VecCreate(comm, OrthQual));
9178   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9179   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9180   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9181   PetscCall(VecSetUp(*OrthQual));
9182   PetscCall(ISDestroy(&glob));
9183   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9184   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9185   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9186   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9187   PetscCall(VecGetDM(cellgeom, &dmCell));
9188   PetscCall(VecGetDM(facegeom, &dmFace));
9189   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9190   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9191     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9192     PetscInt         cellarr[2], *adj = NULL;
9193     PetscScalar     *cArr, *fArr;
9194     PetscReal        minvalc = 1.0, minvalf = 1.0;
9195     PetscFVCellGeom *cg;
9196 
9197     idx[cellIter] = cell - cStart;
9198     cellarr[0]    = cell;
9199     /* Make indexing into cellGeom easier */
9200     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9201     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9202     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9203     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9204     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9205       PetscInt         i;
9206       const PetscInt   neigh  = adj[cellneigh];
9207       PetscReal        normci = 0, normfi = 0, normai = 0;
9208       PetscFVCellGeom *cgneigh;
9209       PetscFVFaceGeom *fg;
9210 
9211       /* Don't count ourselves in the neighbor list */
9212       if (neigh == cell) continue;
9213       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9214       cellarr[1] = neigh;
9215       {
9216         PetscInt        numcovpts;
9217         const PetscInt *covpts;
9218 
9219         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9220         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9221         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9222       }
9223 
9224       /* Compute c_i, f_i and their norms */
9225       for (i = 0; i < nc; i++) {
9226         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9227         fi[i] = fg->centroid[i] - cg->centroid[i];
9228         Ai[i] = fg->normal[i];
9229         normci += PetscPowReal(ci[i], 2);
9230         normfi += PetscPowReal(fi[i], 2);
9231         normai += PetscPowReal(Ai[i], 2);
9232       }
9233       normci = PetscSqrtReal(normci);
9234       normfi = PetscSqrtReal(normfi);
9235       normai = PetscSqrtReal(normai);
9236 
9237       /* Normalize and compute for each face-cell-normal pair */
9238       for (i = 0; i < nc; i++) {
9239         ci[i] = ci[i] / normci;
9240         fi[i] = fi[i] / normfi;
9241         Ai[i] = Ai[i] / normai;
9242         /* PetscAbs because I don't know if normals are guaranteed to point out */
9243         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9244         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9245       }
9246       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9247       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9248     }
9249     PetscCall(PetscFree(adj));
9250     PetscCall(PetscFree2(cArr, fArr));
9251     /* Defer to cell if they're equal */
9252     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9253     if (OrthQualLabel) {
9254       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9255     }
9256   }
9257   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9258   PetscCall(VecAssemblyBegin(*OrthQual));
9259   PetscCall(VecAssemblyEnd(*OrthQual));
9260   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9261   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9262   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9263   if (OrthQualLabel) {
9264     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9265   }
9266   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9267   PetscCall(PetscViewerDestroy(&vwr));
9268   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9269   PetscFunctionReturn(0);
9270 }
9271 
9272 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9273  * interpolator construction */
9274 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9275 {
9276   PetscSection section, newSection, gsection;
9277   PetscSF      sf;
9278   PetscBool    hasConstraints, ghasConstraints;
9279 
9280   PetscFunctionBegin;
9281   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9282   PetscValidPointer(odm, 2);
9283   PetscCall(DMGetLocalSection(dm, &section));
9284   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9285   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9286   if (!ghasConstraints) {
9287     PetscCall(PetscObjectReference((PetscObject)dm));
9288     *odm = dm;
9289     PetscFunctionReturn(0);
9290   }
9291   PetscCall(DMClone(dm, odm));
9292   PetscCall(DMCopyFields(dm, *odm));
9293   PetscCall(DMGetLocalSection(*odm, &newSection));
9294   PetscCall(DMGetPointSF(*odm, &sf));
9295   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9296   PetscCall(DMSetGlobalSection(*odm, gsection));
9297   PetscCall(PetscSectionDestroy(&gsection));
9298   PetscFunctionReturn(0);
9299 }
9300 
9301 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9302 {
9303   DM        dmco, dmfo;
9304   Mat       interpo;
9305   Vec       rscale;
9306   Vec       cglobalo, clocal;
9307   Vec       fglobal, fglobalo, flocal;
9308   PetscBool regular;
9309 
9310   PetscFunctionBegin;
9311   PetscCall(DMGetFullDM(dmc, &dmco));
9312   PetscCall(DMGetFullDM(dmf, &dmfo));
9313   PetscCall(DMSetCoarseDM(dmfo, dmco));
9314   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9315   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9316   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9317   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9318   PetscCall(DMCreateLocalVector(dmc, &clocal));
9319   PetscCall(VecSet(cglobalo, 0.));
9320   PetscCall(VecSet(clocal, 0.));
9321   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9322   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9323   PetscCall(DMCreateLocalVector(dmf, &flocal));
9324   PetscCall(VecSet(fglobal, 0.));
9325   PetscCall(VecSet(fglobalo, 0.));
9326   PetscCall(VecSet(flocal, 0.));
9327   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9328   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9329   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9330   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9331   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9332   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9333   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9334   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9335   *shift = fglobal;
9336   PetscCall(VecDestroy(&flocal));
9337   PetscCall(VecDestroy(&fglobalo));
9338   PetscCall(VecDestroy(&clocal));
9339   PetscCall(VecDestroy(&cglobalo));
9340   PetscCall(VecDestroy(&rscale));
9341   PetscCall(MatDestroy(&interpo));
9342   PetscCall(DMDestroy(&dmfo));
9343   PetscCall(DMDestroy(&dmco));
9344   PetscFunctionReturn(0);
9345 }
9346 
9347 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9348 {
9349   PetscObject shifto;
9350   Vec         shift;
9351 
9352   PetscFunctionBegin;
9353   if (!interp) {
9354     Vec rscale;
9355 
9356     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9357     PetscCall(VecDestroy(&rscale));
9358   } else {
9359     PetscCall(PetscObjectReference((PetscObject)interp));
9360   }
9361   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9362   if (!shifto) {
9363     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9364     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9365     shifto = (PetscObject)shift;
9366     PetscCall(VecDestroy(&shift));
9367   }
9368   shift = (Vec)shifto;
9369   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9370   PetscCall(VecAXPY(fineSol, 1.0, shift));
9371   PetscCall(MatDestroy(&interp));
9372   PetscFunctionReturn(0);
9373 }
9374 
9375 /* Pointwise interpolation
9376      Just code FEM for now
9377      u^f = I u^c
9378      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9379      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9380      I_{ij} = psi^f_i phi^c_j
9381 */
9382 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9383 {
9384   PetscSection gsc, gsf;
9385   PetscInt     m, n;
9386   void        *ctx;
9387   DM           cdm;
9388   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9389 
9390   PetscFunctionBegin;
9391   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9392   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9393   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9394   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9395 
9396   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9397   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9398   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9399   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9400   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9401 
9402   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9403   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9404   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9405   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9406   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9407   if (scaling) {
9408     /* Use naive scaling */
9409     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9410   }
9411   PetscFunctionReturn(0);
9412 }
9413 
9414 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9415 {
9416   VecScatter ctx;
9417 
9418   PetscFunctionBegin;
9419   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9420   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9421   PetscCall(VecScatterDestroy(&ctx));
9422   PetscFunctionReturn(0);
9423 }
9424 
9425 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[])
9426 {
9427   const PetscInt Nc = uOff[1] - uOff[0];
9428   PetscInt       c;
9429   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9430 }
9431 
9432 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9433 {
9434   DM           dmc;
9435   PetscDS      ds;
9436   Vec          ones, locmass;
9437   IS           cellIS;
9438   PetscFormKey key;
9439   PetscInt     depth;
9440 
9441   PetscFunctionBegin;
9442   PetscCall(DMClone(dm, &dmc));
9443   PetscCall(DMCopyDisc(dm, dmc));
9444   PetscCall(DMGetDS(dmc, &ds));
9445   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9446   PetscCall(DMCreateGlobalVector(dmc, mass));
9447   PetscCall(DMGetLocalVector(dmc, &ones));
9448   PetscCall(DMGetLocalVector(dmc, &locmass));
9449   PetscCall(DMPlexGetDepth(dmc, &depth));
9450   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9451   PetscCall(VecSet(locmass, 0.0));
9452   PetscCall(VecSet(ones, 1.0));
9453   key.label = NULL;
9454   key.value = 0;
9455   key.field = 0;
9456   key.part  = 0;
9457   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9458   PetscCall(ISDestroy(&cellIS));
9459   PetscCall(VecSet(*mass, 0.0));
9460   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9461   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9462   PetscCall(DMRestoreLocalVector(dmc, &ones));
9463   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9464   PetscCall(DMDestroy(&dmc));
9465   PetscFunctionReturn(0);
9466 }
9467 
9468 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9469 {
9470   PetscSection gsc, gsf;
9471   PetscInt     m, n;
9472   void        *ctx;
9473   DM           cdm;
9474   PetscBool    regular;
9475 
9476   PetscFunctionBegin;
9477   if (dmFine == dmCoarse) {
9478     DM            dmc;
9479     PetscDS       ds;
9480     PetscWeakForm wf;
9481     Vec           u;
9482     IS            cellIS;
9483     PetscFormKey  key;
9484     PetscInt      depth;
9485 
9486     PetscCall(DMClone(dmFine, &dmc));
9487     PetscCall(DMCopyDisc(dmFine, dmc));
9488     PetscCall(DMGetDS(dmc, &ds));
9489     PetscCall(PetscDSGetWeakForm(ds, &wf));
9490     PetscCall(PetscWeakFormClear(wf));
9491     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9492     PetscCall(DMCreateMatrix(dmc, mass));
9493     PetscCall(DMGetLocalVector(dmc, &u));
9494     PetscCall(DMPlexGetDepth(dmc, &depth));
9495     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9496     PetscCall(MatZeroEntries(*mass));
9497     key.label = NULL;
9498     key.value = 0;
9499     key.field = 0;
9500     key.part  = 0;
9501     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9502     PetscCall(ISDestroy(&cellIS));
9503     PetscCall(DMRestoreLocalVector(dmc, &u));
9504     PetscCall(DMDestroy(&dmc));
9505   } else {
9506     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9507     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9508     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9509     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9510 
9511     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9512     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9513     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9514     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9515 
9516     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9517     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9518     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9519     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9520   }
9521   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9522   PetscFunctionReturn(0);
9523 }
9524 
9525 /*@
9526   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9527 
9528   Input Parameter:
9529 . dm - The `DMPLEX` object
9530 
9531   Output Parameter:
9532 . regular - The flag
9533 
9534   Level: intermediate
9535 
9536 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9537 @*/
9538 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9539 {
9540   PetscFunctionBegin;
9541   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9542   PetscValidBoolPointer(regular, 2);
9543   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9544   PetscFunctionReturn(0);
9545 }
9546 
9547 /*@
9548   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9549 
9550   Input Parameters:
9551 + dm - The `DMPLEX` object
9552 - regular - The flag
9553 
9554   Level: intermediate
9555 
9556 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9557 @*/
9558 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9559 {
9560   PetscFunctionBegin;
9561   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9562   ((DM_Plex *)dm->data)->regularRefinement = regular;
9563   PetscFunctionReturn(0);
9564 }
9565 
9566 /*@
9567   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9568   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9569 
9570   Not Collective
9571 
9572   Input Parameter:
9573 . dm - The `DMPLEX` object
9574 
9575   Output Parameters:
9576 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9577 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9578 
9579   Level: intermediate
9580 
9581 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9582 @*/
9583 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9584 {
9585   DM_Plex *plex = (DM_Plex *)dm->data;
9586 
9587   PetscFunctionBegin;
9588   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9589   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9590   if (anchorSection) *anchorSection = plex->anchorSection;
9591   if (anchorIS) *anchorIS = plex->anchorIS;
9592   PetscFunctionReturn(0);
9593 }
9594 
9595 /*@
9596   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9597   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9598   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9599 
9600   Collective on dm
9601 
9602   Input Parameters:
9603 + dm - The `DMPLEX` object
9604 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9605                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9606 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9607 
9608   Level: intermediate
9609 
9610   Notes:
9611   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9612   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9613 
9614   The reference counts of anchorSection and anchorIS are incremented.
9615 
9616 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9617 @*/
9618 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9619 {
9620   DM_Plex    *plex = (DM_Plex *)dm->data;
9621   PetscMPIInt result;
9622 
9623   PetscFunctionBegin;
9624   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9625   if (anchorSection) {
9626     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9627     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9628     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9629   }
9630   if (anchorIS) {
9631     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9632     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9633     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9634   }
9635 
9636   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9637   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9638   plex->anchorSection = anchorSection;
9639 
9640   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9641   PetscCall(ISDestroy(&plex->anchorIS));
9642   plex->anchorIS = anchorIS;
9643 
9644   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9645     PetscInt        size, a, pStart, pEnd;
9646     const PetscInt *anchors;
9647 
9648     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9649     PetscCall(ISGetLocalSize(anchorIS, &size));
9650     PetscCall(ISGetIndices(anchorIS, &anchors));
9651     for (a = 0; a < size; a++) {
9652       PetscInt p;
9653 
9654       p = anchors[a];
9655       if (p >= pStart && p < pEnd) {
9656         PetscInt dof;
9657 
9658         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9659         if (dof) {
9660           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9661           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9662         }
9663       }
9664     }
9665     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9666   }
9667   /* reset the generic constraints */
9668   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9669   PetscFunctionReturn(0);
9670 }
9671 
9672 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9673 {
9674   PetscSection anchorSection;
9675   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9676 
9677   PetscFunctionBegin;
9678   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9679   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9680   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9681   PetscCall(PetscSectionGetNumFields(section, &numFields));
9682   if (numFields) {
9683     PetscInt f;
9684     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9685 
9686     for (f = 0; f < numFields; f++) {
9687       PetscInt numComp;
9688 
9689       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9690       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9691     }
9692   }
9693   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9694   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9695   pStart = PetscMax(pStart, sStart);
9696   pEnd   = PetscMin(pEnd, sEnd);
9697   pEnd   = PetscMax(pStart, pEnd);
9698   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9699   for (p = pStart; p < pEnd; p++) {
9700     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9701     if (dof) {
9702       PetscCall(PetscSectionGetDof(section, p, &dof));
9703       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9704       for (f = 0; f < numFields; f++) {
9705         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9706         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9707       }
9708     }
9709   }
9710   PetscCall(PetscSectionSetUp(*cSec));
9711   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9712   PetscFunctionReturn(0);
9713 }
9714 
9715 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9716 {
9717   PetscSection    aSec;
9718   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9719   const PetscInt *anchors;
9720   PetscInt        numFields, f;
9721   IS              aIS;
9722   MatType         mtype;
9723   PetscBool       iscuda, iskokkos;
9724 
9725   PetscFunctionBegin;
9726   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9727   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9728   PetscCall(PetscSectionGetStorageSize(section, &n));
9729   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9730   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9731   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9732   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9733   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9734   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9735   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9736   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9737   else mtype = MATSEQAIJ;
9738   PetscCall(MatSetType(*cMat, mtype));
9739   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9740   PetscCall(ISGetIndices(aIS, &anchors));
9741   /* cSec will be a subset of aSec and section */
9742   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9743   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9744   PetscCall(PetscMalloc1(m + 1, &i));
9745   i[0] = 0;
9746   PetscCall(PetscSectionGetNumFields(section, &numFields));
9747   for (p = pStart; p < pEnd; p++) {
9748     PetscInt rDof, rOff, r;
9749 
9750     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9751     if (!rDof) continue;
9752     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9753     if (numFields) {
9754       for (f = 0; f < numFields; f++) {
9755         annz = 0;
9756         for (r = 0; r < rDof; r++) {
9757           a = anchors[rOff + r];
9758           if (a < sStart || a >= sEnd) continue;
9759           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9760           annz += aDof;
9761         }
9762         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9763         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9764         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9765       }
9766     } else {
9767       annz = 0;
9768       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9769       for (q = 0; q < dof; q++) {
9770         a = anchors[rOff + q];
9771         if (a < sStart || a >= sEnd) continue;
9772         PetscCall(PetscSectionGetDof(section, a, &aDof));
9773         annz += aDof;
9774       }
9775       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9776       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9777       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9778     }
9779   }
9780   nnz = i[m];
9781   PetscCall(PetscMalloc1(nnz, &j));
9782   offset = 0;
9783   for (p = pStart; p < pEnd; p++) {
9784     if (numFields) {
9785       for (f = 0; f < numFields; f++) {
9786         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9787         for (q = 0; q < dof; q++) {
9788           PetscInt rDof, rOff, r;
9789           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9790           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9791           for (r = 0; r < rDof; r++) {
9792             PetscInt s;
9793 
9794             a = anchors[rOff + r];
9795             if (a < sStart || a >= sEnd) continue;
9796             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9797             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9798             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9799           }
9800         }
9801       }
9802     } else {
9803       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9804       for (q = 0; q < dof; q++) {
9805         PetscInt rDof, rOff, r;
9806         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9807         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9808         for (r = 0; r < rDof; r++) {
9809           PetscInt s;
9810 
9811           a = anchors[rOff + r];
9812           if (a < sStart || a >= sEnd) continue;
9813           PetscCall(PetscSectionGetDof(section, a, &aDof));
9814           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9815           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9816         }
9817       }
9818     }
9819   }
9820   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9821   PetscCall(PetscFree(i));
9822   PetscCall(PetscFree(j));
9823   PetscCall(ISRestoreIndices(aIS, &anchors));
9824   PetscFunctionReturn(0);
9825 }
9826 
9827 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9828 {
9829   DM_Plex     *plex = (DM_Plex *)dm->data;
9830   PetscSection anchorSection, section, cSec;
9831   Mat          cMat;
9832 
9833   PetscFunctionBegin;
9834   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9835   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9836   if (anchorSection) {
9837     PetscInt Nf;
9838 
9839     PetscCall(DMGetLocalSection(dm, &section));
9840     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9841     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9842     PetscCall(DMGetNumFields(dm, &Nf));
9843     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9844     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9845     PetscCall(PetscSectionDestroy(&cSec));
9846     PetscCall(MatDestroy(&cMat));
9847   }
9848   PetscFunctionReturn(0);
9849 }
9850 
9851 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9852 {
9853   IS           subis;
9854   PetscSection section, subsection;
9855 
9856   PetscFunctionBegin;
9857   PetscCall(DMGetLocalSection(dm, &section));
9858   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9859   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9860   /* Create subdomain */
9861   PetscCall(DMPlexFilter(dm, label, value, subdm));
9862   /* Create submodel */
9863   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9864   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9865   PetscCall(DMSetLocalSection(*subdm, subsection));
9866   PetscCall(PetscSectionDestroy(&subsection));
9867   PetscCall(DMCopyDisc(dm, *subdm));
9868   /* Create map from submodel to global model */
9869   if (is) {
9870     PetscSection    sectionGlobal, subsectionGlobal;
9871     IS              spIS;
9872     const PetscInt *spmap;
9873     PetscInt       *subIndices;
9874     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9875     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9876 
9877     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9878     PetscCall(ISGetIndices(spIS, &spmap));
9879     PetscCall(PetscSectionGetNumFields(section, &Nf));
9880     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9881     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9882     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9883     for (p = pStart; p < pEnd; ++p) {
9884       PetscInt gdof, pSubSize = 0;
9885 
9886       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9887       if (gdof > 0) {
9888         for (f = 0; f < Nf; ++f) {
9889           PetscInt fdof, fcdof;
9890 
9891           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9892           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9893           pSubSize += fdof - fcdof;
9894         }
9895         subSize += pSubSize;
9896         if (pSubSize) {
9897           if (bs < 0) {
9898             bs = pSubSize;
9899           } else if (bs != pSubSize) {
9900             /* Layout does not admit a pointwise block size */
9901             bs = 1;
9902           }
9903         }
9904       }
9905     }
9906     /* Must have same blocksize on all procs (some might have no points) */
9907     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
9908     bsLocal[1] = bs;
9909     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
9910     if (bsMinMax[0] != bsMinMax[1]) {
9911       bs = 1;
9912     } else {
9913       bs = bsMinMax[0];
9914     }
9915     PetscCall(PetscMalloc1(subSize, &subIndices));
9916     for (p = pStart; p < pEnd; ++p) {
9917       PetscInt gdof, goff;
9918 
9919       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9920       if (gdof > 0) {
9921         const PetscInt point = spmap[p];
9922 
9923         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9924         for (f = 0; f < Nf; ++f) {
9925           PetscInt fdof, fcdof, fc, f2, poff = 0;
9926 
9927           /* Can get rid of this loop by storing field information in the global section */
9928           for (f2 = 0; f2 < f; ++f2) {
9929             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9930             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9931             poff += fdof - fcdof;
9932           }
9933           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9934           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9935           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
9936         }
9937       }
9938     }
9939     PetscCall(ISRestoreIndices(spIS, &spmap));
9940     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9941     if (bs > 1) {
9942       /* We need to check that the block size does not come from non-contiguous fields */
9943       PetscInt i, j, set = 1;
9944       for (i = 0; i < subSize; i += bs) {
9945         for (j = 0; j < bs; ++j) {
9946           if (subIndices[i + j] != subIndices[i] + j) {
9947             set = 0;
9948             break;
9949           }
9950         }
9951       }
9952       if (set) PetscCall(ISSetBlockSize(*is, bs));
9953     }
9954     /* Attach nullspace */
9955     for (f = 0; f < Nf; ++f) {
9956       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9957       if ((*subdm)->nullspaceConstructors[f]) break;
9958     }
9959     if (f < Nf) {
9960       MatNullSpace nullSpace;
9961       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9962 
9963       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
9964       PetscCall(MatNullSpaceDestroy(&nullSpace));
9965     }
9966   }
9967   PetscFunctionReturn(0);
9968 }
9969 
9970 /*@
9971   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9972 
9973   Input Parameters:
9974 + dm - The `DM`
9975 - dummy - unused argument
9976 
9977   Options Database Key:
9978 . -dm_plex_monitor_throughput - Activate the monitor
9979 
9980   Level: developer
9981 
9982 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
9983 @*/
9984 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
9985 {
9986 #if defined(PETSC_USE_LOG)
9987   PetscStageLog      stageLog;
9988   PetscLogEvent      event;
9989   PetscLogStage      stage;
9990   PetscEventPerfInfo eventInfo;
9991   PetscReal          cellRate, flopRate;
9992   PetscInt           cStart, cEnd, Nf, N;
9993   const char        *name;
9994 #endif
9995 
9996   PetscFunctionBegin;
9997   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9998 #if defined(PETSC_USE_LOG)
9999   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10000   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10001   PetscCall(DMGetNumFields(dm, &Nf));
10002   PetscCall(PetscLogGetStageLog(&stageLog));
10003   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10004   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10005   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10006   N        = (cEnd - cStart) * Nf * eventInfo.count;
10007   flopRate = eventInfo.flops / eventInfo.time;
10008   cellRate = N / eventInfo.time;
10009   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)));
10010 #else
10011   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10012 #endif
10013   PetscFunctionReturn(0);
10014 }
10015