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