xref: /petsc/src/dm/impls/plex/plex.c (revision 6c2b77d522d8aa5c8b27f04fddd7150d0d6755fb)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm      - The `DMPLEX` object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(0);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(0);
49 }
50 
51 /*@
52   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
53 
54   Input Parameters:
55 + dm     - The `DMPLEX` object
56 - height - The cell height in the Plex, 0 is the default
57 
58   Output Parameters:
59 + cStart - The first "normal" cell
60 - cEnd   - The upper bound on "normal"" cells
61 
62   Level: developer
63 
64   Note:
65   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
66 
67 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(0);
106 }
107 
108 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
109 {
110   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
111   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
112 
113   PetscFunctionBegin;
114   *ft = PETSC_VTK_INVALID;
115   PetscCall(DMGetCoordinateDim(dm, &cdim));
116   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
118   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
119   if (field >= 0) {
120     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
121     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
122   } else {
123     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
124     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
125   }
126   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(0);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective on dm
154 
155   Input Parameters:
156 + dm - The `DMPLEX` object
157 . n  - The number of vectors
158 . u  - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(0);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrcpy(tmpname, vname));
202         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
203         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(0);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(0);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *coords, *array;
253   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(VecGetArrayRead(coordinates, &coords));
278   for (c = 0; c < N; c += dim) {
279     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
280     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
281     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
282     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
283   }
284   PetscCall(VecRestoreArrayRead(coordinates, &coords));
285   PetscCall(PetscDrawClear(draw));
286 
287   /* Could implement something like DMDASelectFields() */
288   for (f = 0; f < Nf; ++f) {
289     DM          fdm = dm;
290     Vec         fv  = v;
291     IS          fis;
292     char        prefix[PETSC_MAX_PATH_LEN];
293     const char *fname;
294 
295     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
296     PetscCall(PetscSectionGetFieldName(s, f, &fname));
297 
298     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
299     else prefix[0] = '\0';
300     if (Nf > 1) {
301       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
302       PetscCall(VecGetSubVector(v, fis, &fv));
303       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
304       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
305     }
306     for (comp = 0; comp < Nc; ++comp, ++w) {
307       PetscInt nmax = 2;
308 
309       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
310       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
311       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
312       PetscCall(PetscDrawSetTitle(draw, title));
313 
314       /* TODO Get max and min only for this component */
315       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
316       if (!flg) {
317         PetscCall(VecMin(fv, NULL, &vbound[0]));
318         PetscCall(VecMax(fv, NULL, &vbound[1]));
319         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
320       }
321       PetscCall(PetscDrawGetPopup(draw, &popup));
322       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
323       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
324 
325       PetscCall(VecGetArrayRead(fv, &array));
326       for (c = cStart; c < cEnd; ++c) {
327         PetscScalar *coords = NULL, *a   = NULL;
328         PetscInt     numCoords, color[4] = {-1, -1, -1, -1};
329 
330         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
331         if (a) {
332           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
333           color[1] = color[2] = color[3] = color[0];
334         } else {
335           PetscScalar *vals = NULL;
336           PetscInt     numVals, va;
337 
338           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
339           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
340           switch (numVals / Nc) {
341           case 3: /* P1 Triangle */
342           case 4: /* P1 Quadrangle */
343             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
344             break;
345           case 6: /* P2 Triangle */
346           case 8: /* P2 Quadrangle */
347             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
348             break;
349           default:
350             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
351           }
352           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
353         }
354         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
355         switch (numCoords) {
356         case 6:
357         case 12: /* Localized triangle */
358           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
359           break;
360         case 8:
361         case 16: /* Localized quadrilateral */
362           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
363           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
364           break;
365         default:
366           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
367         }
368         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
369       }
370       PetscCall(VecRestoreArrayRead(fv, &array));
371       PetscCall(PetscDrawFlush(draw));
372       PetscCall(PetscDrawPause(draw));
373       PetscCall(PetscDrawSave(draw));
374     }
375     if (Nf > 1) {
376       PetscCall(VecRestoreSubVector(v, fis, &fv));
377       PetscCall(ISDestroy(&fis));
378       PetscCall(DMDestroy(&fdm));
379     }
380   }
381   PetscFunctionReturn(0);
382 }
383 
384 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
385 {
386   DM        dm;
387   PetscDraw draw;
388   PetscInt  dim;
389   PetscBool isnull;
390 
391   PetscFunctionBegin;
392   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
393   PetscCall(PetscDrawIsNull(draw, &isnull));
394   if (isnull) PetscFunctionReturn(0);
395 
396   PetscCall(VecGetDM(v, &dm));
397   PetscCall(DMGetCoordinateDim(dm, &dim));
398   switch (dim) {
399   case 1:
400     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
401     break;
402   case 2:
403     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
404     break;
405   default:
406     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
407   }
408   PetscFunctionReturn(0);
409 }
410 
411 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
412 {
413   DM                      dm;
414   Vec                     locv;
415   const char             *name;
416   PetscSection            section;
417   PetscInt                pStart, pEnd;
418   PetscInt                numFields;
419   PetscViewerVTKFieldType ft;
420 
421   PetscFunctionBegin;
422   PetscCall(VecGetDM(v, &dm));
423   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
424   PetscCall(PetscObjectGetName((PetscObject)v, &name));
425   PetscCall(PetscObjectSetName((PetscObject)locv, name));
426   PetscCall(VecCopy(v, locv));
427   PetscCall(DMGetLocalSection(dm, &section));
428   PetscCall(PetscSectionGetNumFields(section, &numFields));
429   if (!numFields) {
430     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
431     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
432   } else {
433     PetscInt f;
434 
435     for (f = 0; f < numFields; f++) {
436       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
437       if (ft == PETSC_VTK_INVALID) continue;
438       PetscCall(PetscObjectReference((PetscObject)locv));
439       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
440     }
441     PetscCall(VecDestroy(&locv));
442   }
443   PetscFunctionReturn(0);
444 }
445 
446 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
447 {
448   DM        dm;
449   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
450 
451   PetscFunctionBegin;
452   PetscCall(VecGetDM(v, &dm));
453   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
454   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
455   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
456   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
457   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
458   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
459   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
460     PetscInt    i, numFields;
461     PetscObject fe;
462     PetscBool   fem  = PETSC_FALSE;
463     Vec         locv = v;
464     const char *name;
465     PetscInt    step;
466     PetscReal   time;
467 
468     PetscCall(DMGetNumFields(dm, &numFields));
469     for (i = 0; i < numFields; i++) {
470       PetscCall(DMGetField(dm, i, NULL, &fe));
471       if (fe->classid == PETSCFE_CLASSID) {
472         fem = PETSC_TRUE;
473         break;
474       }
475     }
476     if (fem) {
477       PetscObject isZero;
478 
479       PetscCall(DMGetLocalVector(dm, &locv));
480       PetscCall(PetscObjectGetName((PetscObject)v, &name));
481       PetscCall(PetscObjectSetName((PetscObject)locv, name));
482       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
483       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
484       PetscCall(VecCopy(v, locv));
485       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
486       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
487     }
488     if (isvtk) {
489       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
490     } else if (ishdf5) {
491 #if defined(PETSC_HAVE_HDF5)
492       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
493 #else
494       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
495 #endif
496     } else if (isdraw) {
497       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
498     } else if (isglvis) {
499       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
500       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
501       PetscCall(VecView_GLVis(locv, viewer));
502     } else if (iscgns) {
503 #if defined(PETSC_HAVE_CGNS)
504       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
505 #else
506       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
507 #endif
508     }
509     if (fem) {
510       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
511       PetscCall(DMRestoreLocalVector(dm, &locv));
512     }
513   } else {
514     PetscBool isseq;
515 
516     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
517     if (isseq) PetscCall(VecView_Seq(v, viewer));
518     else PetscCall(VecView_MPI(v, viewer));
519   }
520   PetscFunctionReturn(0);
521 }
522 
523 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
524 {
525   DM        dm;
526   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
527 
528   PetscFunctionBegin;
529   PetscCall(VecGetDM(v, &dm));
530   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
532   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
533   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
534   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
535   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
536   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
537   if (isvtk || isdraw || isglvis || iscgns) {
538     Vec         locv;
539     PetscObject isZero;
540     const char *name;
541 
542     PetscCall(DMGetLocalVector(dm, &locv));
543     PetscCall(PetscObjectGetName((PetscObject)v, &name));
544     PetscCall(PetscObjectSetName((PetscObject)locv, name));
545     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
546     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
547     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
548     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
549     PetscCall(VecView_Plex_Local(locv, viewer));
550     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
551     PetscCall(DMRestoreLocalVector(dm, &locv));
552   } else if (ishdf5) {
553 #if defined(PETSC_HAVE_HDF5)
554     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
555 #else
556     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
557 #endif
558   } else if (isexodusii) {
559 #if defined(PETSC_HAVE_EXODUSII)
560     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
561 #else
562     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
563 #endif
564   } else {
565     PetscBool isseq;
566 
567     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
568     if (isseq) PetscCall(VecView_Seq(v, viewer));
569     else PetscCall(VecView_MPI(v, viewer));
570   }
571   PetscFunctionReturn(0);
572 }
573 
574 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
575 {
576   DM                dm;
577   MPI_Comm          comm;
578   PetscViewerFormat format;
579   Vec               v;
580   PetscBool         isvtk, ishdf5;
581 
582   PetscFunctionBegin;
583   PetscCall(VecGetDM(originalv, &dm));
584   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
585   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
586   PetscCall(PetscViewerGetFormat(viewer, &format));
587   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
588   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
589   if (format == PETSC_VIEWER_NATIVE) {
590     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
591     /* this need a better fix */
592     if (dm->useNatural) {
593       if (dm->sfNatural) {
594         const char *vecname;
595         PetscInt    n, nroots;
596 
597         PetscCall(VecGetLocalSize(originalv, &n));
598         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
599         if (n == nroots) {
600           PetscCall(DMGetGlobalVector(dm, &v));
601           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
602           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
603           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
604           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
605         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
606       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
607     } else v = originalv;
608   } else v = originalv;
609 
610   if (ishdf5) {
611 #if defined(PETSC_HAVE_HDF5)
612     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
613 #else
614     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
615 #endif
616   } else if (isvtk) {
617     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
618   } else {
619     PetscBool isseq;
620 
621     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
622     if (isseq) PetscCall(VecView_Seq(v, viewer));
623     else PetscCall(VecView_MPI(v, viewer));
624   }
625   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
626   PetscFunctionReturn(0);
627 }
628 
629 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
630 {
631   DM        dm;
632   PetscBool ishdf5;
633 
634   PetscFunctionBegin;
635   PetscCall(VecGetDM(v, &dm));
636   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
638   if (ishdf5) {
639     DM          dmBC;
640     Vec         gv;
641     const char *name;
642 
643     PetscCall(DMGetOutputDM(dm, &dmBC));
644     PetscCall(DMGetGlobalVector(dmBC, &gv));
645     PetscCall(PetscObjectGetName((PetscObject)v, &name));
646     PetscCall(PetscObjectSetName((PetscObject)gv, name));
647     PetscCall(VecLoad_Default(gv, viewer));
648     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
649     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
650     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
651   } else PetscCall(VecLoad_Default(v, viewer));
652   PetscFunctionReturn(0);
653 }
654 
655 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
656 {
657   DM        dm;
658   PetscBool ishdf5, isexodusii;
659 
660   PetscFunctionBegin;
661   PetscCall(VecGetDM(v, &dm));
662   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
663   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
664   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
665   if (ishdf5) {
666 #if defined(PETSC_HAVE_HDF5)
667     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
668 #else
669     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
670 #endif
671   } else if (isexodusii) {
672 #if defined(PETSC_HAVE_EXODUSII)
673     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
674 #else
675     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
676 #endif
677   } else PetscCall(VecLoad_Default(v, viewer));
678   PetscFunctionReturn(0);
679 }
680 
681 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
682 {
683   DM                dm;
684   PetscViewerFormat format;
685   PetscBool         ishdf5;
686 
687   PetscFunctionBegin;
688   PetscCall(VecGetDM(originalv, &dm));
689   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
690   PetscCall(PetscViewerGetFormat(viewer, &format));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
692   if (format == PETSC_VIEWER_NATIVE) {
693     if (dm->useNatural) {
694       if (dm->sfNatural) {
695         if (ishdf5) {
696 #if defined(PETSC_HAVE_HDF5)
697           Vec         v;
698           const char *vecname;
699 
700           PetscCall(DMGetGlobalVector(dm, &v));
701           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
702           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
703           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
704           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
705           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
706           PetscCall(DMRestoreGlobalVector(dm, &v));
707 #else
708           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
709 #endif
710         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
711       }
712     } else PetscCall(VecLoad_Default(originalv, viewer));
713   }
714   PetscFunctionReturn(0);
715 }
716 
717 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
718 {
719   PetscSection       coordSection;
720   Vec                coordinates;
721   DMLabel            depthLabel, celltypeLabel;
722   const char        *name[4];
723   const PetscScalar *a;
724   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
725 
726   PetscFunctionBegin;
727   PetscCall(DMGetDimension(dm, &dim));
728   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
729   PetscCall(DMGetCoordinateSection(dm, &coordSection));
730   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
731   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
732   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
733   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
734   PetscCall(VecGetArrayRead(coordinates, &a));
735   name[0]       = "vertex";
736   name[1]       = "edge";
737   name[dim - 1] = "face";
738   name[dim]     = "cell";
739   for (c = cStart; c < cEnd; ++c) {
740     PetscInt *closure = NULL;
741     PetscInt  closureSize, cl, ct;
742 
743     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
744     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
745     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
746     PetscCall(PetscViewerASCIIPushTab(viewer));
747     for (cl = 0; cl < closureSize * 2; cl += 2) {
748       PetscInt point = closure[cl], depth, dof, off, d, p;
749 
750       if ((point < pStart) || (point >= pEnd)) continue;
751       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
752       if (!dof) continue;
753       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
754       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
755       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
756       for (p = 0; p < dof / dim; ++p) {
757         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
758         for (d = 0; d < dim; ++d) {
759           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
760           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
761         }
762         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
763       }
764       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
765     }
766     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
767     PetscCall(PetscViewerASCIIPopTab(viewer));
768   }
769   PetscCall(VecRestoreArrayRead(coordinates, &a));
770   PetscFunctionReturn(0);
771 }
772 
773 typedef enum {
774   CS_CARTESIAN,
775   CS_POLAR,
776   CS_CYLINDRICAL,
777   CS_SPHERICAL
778 } CoordSystem;
779 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
780 
781 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
782 {
783   PetscInt i;
784 
785   PetscFunctionBegin;
786   if (dim > 3) {
787     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
788   } else {
789     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
790 
791     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
792     switch (cs) {
793     case CS_CARTESIAN:
794       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
795       break;
796     case CS_POLAR:
797       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       break;
801     case CS_CYLINDRICAL:
802       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
803       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
804       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
805       trcoords[2] = coords[2];
806       break;
807     case CS_SPHERICAL:
808       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
809       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
810       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
811       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
812       break;
813     }
814     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
815   }
816   PetscFunctionReturn(0);
817 }
818 
819 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
820 {
821   DM_Plex          *mesh = (DM_Plex *)dm->data;
822   DM                cdm, cdmCell;
823   PetscSection      coordSection, coordSectionCell;
824   Vec               coordinates, coordinatesCell;
825   PetscViewerFormat format;
826 
827   PetscFunctionBegin;
828   PetscCall(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     PetscCall(PetscViewerFlush(viewer));
945   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
946     const char  *name, *color;
947     const char  *defcolors[3]  = {"gray", "orange", "green"};
948     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
949     char         lname[PETSC_MAX_PATH_LEN];
950     PetscReal    scale      = 2.0;
951     PetscReal    tikzscale  = 1.0;
952     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
953     double       tcoords[3];
954     PetscScalar *coords;
955     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
956     PetscMPIInt  rank, size;
957     char       **names, **colors, **lcolors;
958     PetscBool    flg, lflg;
959     PetscBT      wp = NULL;
960     PetscInt     pEnd, pStart;
961 
962     PetscCall(DMGetCoordinateDM(dm, &cdm));
963     PetscCall(DMGetCoordinateSection(dm, &coordSection));
964     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
965     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
966     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
967     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
968     PetscCall(DMGetDimension(dm, &dim));
969     PetscCall(DMPlexGetDepth(dm, &depth));
970     PetscCall(DMGetNumLabels(dm, &numLabels));
971     numLabels  = PetscMax(numLabels, 10);
972     numColors  = 10;
973     numLColors = 10;
974     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
975     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
976     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
977     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
978     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
979     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
980     n = 4;
981     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
982     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
983     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
984     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
985     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
986     if (!useLabels) numLabels = 0;
987     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
988     if (!useColors) {
989       numColors = 3;
990       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
991     }
992     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
993     if (!useColors) {
994       numLColors = 4;
995       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
996     }
997     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
998     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
999     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1000     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1001     if (depth < dim) plotEdges = PETSC_FALSE;
1002     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1003 
1004     /* filter points with labelvalue != labeldefaultvalue */
1005     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1006     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1007     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1008     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1009     if (lflg) {
1010       DMLabel lbl;
1011 
1012       PetscCall(DMGetLabel(dm, lname, &lbl));
1013       if (lbl) {
1014         PetscInt val, defval;
1015 
1016         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1017         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1018         for (c = pStart; c < pEnd; c++) {
1019           PetscInt *closure = NULL;
1020           PetscInt  closureSize;
1021 
1022           PetscCall(DMLabelGetValue(lbl, c, &val));
1023           if (val == defval) continue;
1024 
1025           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1026           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1027           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1028         }
1029       }
1030     }
1031 
1032     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1033     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1034     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1035     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1036 \\documentclass[tikz]{standalone}\n\n\
1037 \\usepackage{pgflibraryshapes}\n\
1038 \\usetikzlibrary{backgrounds}\n\
1039 \\usetikzlibrary{arrows}\n\
1040 \\begin{document}\n"));
1041     if (size > 1) {
1042       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1043       for (p = 0; p < size; ++p) {
1044         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1045         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1046       }
1047       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1048     }
1049     if (drawHasse) {
1050       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1051 
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1060       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1061       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1062       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1063       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1064     }
1065     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1066 
1067     /* Plot vertices */
1068     PetscCall(VecGetArray(coordinates, &coords));
1069     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1070     for (v = vStart; v < vEnd; ++v) {
1071       PetscInt  off, dof, d;
1072       PetscBool isLabeled = PETSC_FALSE;
1073 
1074       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1075       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1076       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1077       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1078       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1079       for (d = 0; d < dof; ++d) {
1080         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1081         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1082       }
1083       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1084       if (dim == 3) {
1085         PetscReal tmp = tcoords[1];
1086         tcoords[1]    = tcoords[2];
1087         tcoords[2]    = -tmp;
1088       }
1089       for (d = 0; d < dof; ++d) {
1090         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1091         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1092       }
1093       if (drawHasse) color = colors[0 % numColors];
1094       else color = colors[rank % numColors];
1095       for (l = 0; l < numLabels; ++l) {
1096         PetscInt val;
1097         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1098         if (val >= 0) {
1099           color     = lcolors[l % numLColors];
1100           isLabeled = PETSC_TRUE;
1101           break;
1102         }
1103       }
1104       if (drawNumbers[0]) {
1105         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1106       } else if (drawColors[0]) {
1107         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1108       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1109     }
1110     PetscCall(VecRestoreArray(coordinates, &coords));
1111     PetscCall(PetscViewerFlush(viewer));
1112     /* Plot edges */
1113     if (plotEdges) {
1114       PetscCall(VecGetArray(coordinates, &coords));
1115       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1116       for (e = eStart; e < eEnd; ++e) {
1117         const PetscInt *cone;
1118         PetscInt        coneSize, offA, offB, dof, d;
1119 
1120         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1121         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1122         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1123         PetscCall(DMPlexGetCone(dm, e, &cone));
1124         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1125         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1126         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1127         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1128         for (d = 0; d < dof; ++d) {
1129           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1130           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1131         }
1132         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1133         if (dim == 3) {
1134           PetscReal tmp = tcoords[1];
1135           tcoords[1]    = tcoords[2];
1136           tcoords[2]    = -tmp;
1137         }
1138         for (d = 0; d < dof; ++d) {
1139           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1140           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1141         }
1142         if (drawHasse) color = colors[1 % numColors];
1143         else color = colors[rank % numColors];
1144         for (l = 0; l < numLabels; ++l) {
1145           PetscInt val;
1146           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1147           if (val >= 0) {
1148             color = lcolors[l % numLColors];
1149             break;
1150           }
1151         }
1152         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1153       }
1154       PetscCall(VecRestoreArray(coordinates, &coords));
1155       PetscCall(PetscViewerFlush(viewer));
1156       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1157     }
1158     /* Plot cells */
1159     if (dim == 3 || !drawNumbers[1]) {
1160       for (e = eStart; e < eEnd; ++e) {
1161         const PetscInt *cone;
1162 
1163         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1164         color = colors[rank % numColors];
1165         for (l = 0; l < numLabels; ++l) {
1166           PetscInt val;
1167           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1168           if (val >= 0) {
1169             color = lcolors[l % numLColors];
1170             break;
1171           }
1172         }
1173         PetscCall(DMPlexGetCone(dm, e, &cone));
1174         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1175       }
1176     } else {
1177       DMPolytopeType ct;
1178 
1179       /* Drawing a 2D polygon */
1180       for (c = cStart; c < cEnd; ++c) {
1181         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1182         PetscCall(DMPlexGetCellType(dm, c, &ct));
1183         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1184           const PetscInt *cone;
1185           PetscInt        coneSize, e;
1186 
1187           PetscCall(DMPlexGetCone(dm, c, &cone));
1188           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1189           for (e = 0; e < coneSize; ++e) {
1190             const PetscInt *econe;
1191 
1192             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1193             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));
1194           }
1195         } else {
1196           PetscInt *closure = NULL;
1197           PetscInt  closureSize, Nv = 0, v;
1198 
1199           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1200           for (p = 0; p < closureSize * 2; p += 2) {
1201             const PetscInt point = closure[p];
1202 
1203             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1204           }
1205           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1206           for (v = 0; v <= Nv; ++v) {
1207             const PetscInt vertex = closure[v % Nv];
1208 
1209             if (v > 0) {
1210               if (plotEdges) {
1211                 const PetscInt *edge;
1212                 PetscInt        endpoints[2], ne;
1213 
1214                 endpoints[0] = closure[v - 1];
1215                 endpoints[1] = vertex;
1216                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1217                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1218                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1219                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1220               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1221             }
1222             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1223           }
1224           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1225           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1226         }
1227       }
1228     }
1229     for (c = cStart; c < cEnd; ++c) {
1230       double             ccoords[3] = {0.0, 0.0, 0.0};
1231       PetscBool          isLabeled  = PETSC_FALSE;
1232       PetscScalar       *cellCoords = NULL;
1233       const PetscScalar *array;
1234       PetscInt           numCoords, cdim, d;
1235       PetscBool          isDG;
1236 
1237       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1238       PetscCall(DMGetCoordinateDim(dm, &cdim));
1239       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1240       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1241       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1242       for (p = 0; p < numCoords / cdim; ++p) {
1243         for (d = 0; d < cdim; ++d) {
1244           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1245           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1246         }
1247         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1248         if (cdim == 3) {
1249           PetscReal tmp = tcoords[1];
1250           tcoords[1]    = tcoords[2];
1251           tcoords[2]    = -tmp;
1252         }
1253         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1254       }
1255       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1256       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1257       for (d = 0; d < cdim; ++d) {
1258         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1259         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1260       }
1261       if (drawHasse) color = colors[depth % numColors];
1262       else color = colors[rank % numColors];
1263       for (l = 0; l < numLabels; ++l) {
1264         PetscInt val;
1265         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1266         if (val >= 0) {
1267           color     = lcolors[l % numLColors];
1268           isLabeled = PETSC_TRUE;
1269           break;
1270         }
1271       }
1272       if (drawNumbers[dim]) {
1273         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1274       } else if (drawColors[dim]) {
1275         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1276       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1277     }
1278     if (drawHasse) {
1279       color = colors[depth % numColors];
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1281       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1282       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1285 
1286       color = colors[1 % numColors];
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1288       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1289       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1292 
1293       color = colors[0 % numColors];
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1295       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1296       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1297       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1298       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1299 
1300       for (p = pStart; p < pEnd; ++p) {
1301         const PetscInt *cone;
1302         PetscInt        coneSize, cp;
1303 
1304         PetscCall(DMPlexGetCone(dm, p, &cone));
1305         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1306         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1307       }
1308     }
1309     PetscCall(PetscViewerFlush(viewer));
1310     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1311     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1312     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1313     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1314     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1315     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1316     PetscCall(PetscFree3(names, colors, lcolors));
1317     PetscCall(PetscBTDestroy(&wp));
1318   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1319     Vec                    cown, acown;
1320     VecScatter             sct;
1321     ISLocalToGlobalMapping g2l;
1322     IS                     gid, acis;
1323     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1324     MPI_Group              ggroup, ngroup;
1325     PetscScalar           *array, nid;
1326     const PetscInt        *idxs;
1327     PetscInt              *idxs2, *start, *adjacency, *work;
1328     PetscInt64             lm[3], gm[3];
1329     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1330     PetscMPIInt            d1, d2, rank;
1331 
1332     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1333     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1334 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1335     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1336 #endif
1337     if (ncomm != MPI_COMM_NULL) {
1338       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1339       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1340       d1 = 0;
1341       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1342       nid = d2;
1343       PetscCallMPI(MPI_Group_free(&ggroup));
1344       PetscCallMPI(MPI_Group_free(&ngroup));
1345       PetscCallMPI(MPI_Comm_free(&ncomm));
1346     } else nid = 0.0;
1347 
1348     /* Get connectivity */
1349     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1350     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1351 
1352     /* filter overlapped local cells */
1353     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1354     PetscCall(ISGetIndices(gid, &idxs));
1355     PetscCall(ISGetLocalSize(gid, &cum));
1356     PetscCall(PetscMalloc1(cum, &idxs2));
1357     for (c = cStart, cum = 0; c < cEnd; c++) {
1358       if (idxs[c - cStart] < 0) continue;
1359       idxs2[cum++] = idxs[c - cStart];
1360     }
1361     PetscCall(ISRestoreIndices(gid, &idxs));
1362     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1363     PetscCall(ISDestroy(&gid));
1364     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1365 
1366     /* support for node-aware cell locality */
1367     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1368     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1369     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1370     PetscCall(VecGetArray(cown, &array));
1371     for (c = 0; c < numVertices; c++) array[c] = nid;
1372     PetscCall(VecRestoreArray(cown, &array));
1373     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1374     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1375     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1376     PetscCall(ISDestroy(&acis));
1377     PetscCall(VecScatterDestroy(&sct));
1378     PetscCall(VecDestroy(&cown));
1379 
1380     /* compute edgeCut */
1381     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1382     PetscCall(PetscMalloc1(cum, &work));
1383     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1384     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1385     PetscCall(ISDestroy(&gid));
1386     PetscCall(VecGetArray(acown, &array));
1387     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1388       PetscInt totl;
1389 
1390       totl = start[c + 1] - start[c];
1391       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1392       for (i = 0; i < totl; i++) {
1393         if (work[i] < 0) {
1394           ect += 1;
1395           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1396         }
1397       }
1398     }
1399     PetscCall(PetscFree(work));
1400     PetscCall(VecRestoreArray(acown, &array));
1401     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1402     lm[1] = -numVertices;
1403     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1404     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1405     lm[0] = ect;                     /* edgeCut */
1406     lm[1] = ectn;                    /* node-aware edgeCut */
1407     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1408     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1409     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1410 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1411     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1412 #else
1413     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1414 #endif
1415     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1416     PetscCall(PetscFree(start));
1417     PetscCall(PetscFree(adjacency));
1418     PetscCall(VecDestroy(&acown));
1419   } else {
1420     const char    *name;
1421     PetscInt      *sizes, *hybsizes, *ghostsizes;
1422     PetscInt       locDepth, depth, cellHeight, dim, d;
1423     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1424     PetscInt       numLabels, l, maxSize = 17;
1425     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1426     MPI_Comm       comm;
1427     PetscMPIInt    size, rank;
1428 
1429     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1430     PetscCallMPI(MPI_Comm_size(comm, &size));
1431     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1432     PetscCall(DMGetDimension(dm, &dim));
1433     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1434     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1435     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1436     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1437     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1438     PetscCall(DMPlexGetDepth(dm, &locDepth));
1439     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1440     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1441     gcNum = gcEnd - gcStart;
1442     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1443     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1444     for (d = 0; d <= depth; d++) {
1445       PetscInt Nc[2] = {0, 0}, ict;
1446 
1447       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1448       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1449       ict = ct0;
1450       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1451       ct0 = (DMPolytopeType)ict;
1452       for (p = pStart; p < pEnd; ++p) {
1453         DMPolytopeType ct;
1454 
1455         PetscCall(DMPlexGetCellType(dm, p, &ct));
1456         if (ct == ct0) ++Nc[0];
1457         else ++Nc[1];
1458       }
1459       if (size < maxSize) {
1460         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1461         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1462         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1463         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1464         for (p = 0; p < size; ++p) {
1465           if (rank == 0) {
1466             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1467             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1468             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1469           }
1470         }
1471       } else {
1472         PetscInt locMinMax[2];
1473 
1474         locMinMax[0] = Nc[0] + Nc[1];
1475         locMinMax[1] = Nc[0] + Nc[1];
1476         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1477         locMinMax[0] = Nc[1];
1478         locMinMax[1] = Nc[1];
1479         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1480         if (d == depth) {
1481           locMinMax[0] = gcNum;
1482           locMinMax[1] = gcNum;
1483           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1484         }
1485         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1486         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1487         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1488         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1489       }
1490       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1491     }
1492     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1493     {
1494       const PetscReal *maxCell;
1495       const PetscReal *L;
1496       PetscBool        localized;
1497 
1498       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1499       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1500       if (L || localized) {
1501         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1502         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1503         if (L) {
1504           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1505           for (d = 0; d < dim; ++d) {
1506             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1507             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1508           }
1509           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1510         }
1511         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1512         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1513       }
1514     }
1515     PetscCall(DMGetNumLabels(dm, &numLabels));
1516     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1517     for (l = 0; l < numLabels; ++l) {
1518       DMLabel         label;
1519       const char     *name;
1520       IS              valueIS;
1521       const PetscInt *values;
1522       PetscInt        numValues, v;
1523 
1524       PetscCall(DMGetLabelName(dm, l, &name));
1525       PetscCall(DMGetLabel(dm, name, &label));
1526       PetscCall(DMLabelGetNumValues(label, &numValues));
1527       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1528       PetscCall(DMLabelGetValueIS(label, &valueIS));
1529       PetscCall(ISGetIndices(valueIS, &values));
1530       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1531       for (v = 0; v < numValues; ++v) {
1532         PetscInt size;
1533 
1534         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1535         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1536         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1537       }
1538       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1539       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1540       PetscCall(ISRestoreIndices(valueIS, &values));
1541       PetscCall(ISDestroy(&valueIS));
1542     }
1543     {
1544       char    **labelNames;
1545       PetscInt  Nl = numLabels;
1546       PetscBool flg;
1547 
1548       PetscCall(PetscMalloc1(Nl, &labelNames));
1549       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1550       for (l = 0; l < Nl; ++l) {
1551         DMLabel label;
1552 
1553         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1554         if (flg) {
1555           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1556           PetscCall(DMLabelView(label, viewer));
1557         }
1558         PetscCall(PetscFree(labelNames[l]));
1559       }
1560       PetscCall(PetscFree(labelNames));
1561     }
1562     /* If no fields are specified, people do not want to see adjacency */
1563     if (dm->Nf) {
1564       PetscInt f;
1565 
1566       for (f = 0; f < dm->Nf; ++f) {
1567         const char *name;
1568 
1569         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1570         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1571         PetscCall(PetscViewerASCIIPushTab(viewer));
1572         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1573         if (dm->fields[f].adjacency[0]) {
1574           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1575           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1576         } else {
1577           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1578           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1579         }
1580         PetscCall(PetscViewerASCIIPopTab(viewer));
1581       }
1582     }
1583     PetscCall(DMGetCoarseDM(dm, &cdm));
1584     if (cdm) {
1585       PetscCall(PetscViewerASCIIPushTab(viewer));
1586       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1587       PetscCall(DMPlexView_Ascii(cdm, viewer));
1588       PetscCall(PetscViewerASCIIPopTab(viewer));
1589     }
1590   }
1591   PetscFunctionReturn(0);
1592 }
1593 
1594 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1595 {
1596   DMPolytopeType ct;
1597   PetscMPIInt    rank;
1598   PetscInt       cdim;
1599 
1600   PetscFunctionBegin;
1601   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1602   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1603   PetscCall(DMGetCoordinateDim(dm, &cdim));
1604   switch (ct) {
1605   case DM_POLYTOPE_SEGMENT:
1606   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1607     switch (cdim) {
1608     case 1: {
1609       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1610       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1611 
1612       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1613       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1614       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1615     } break;
1616     case 2: {
1617       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1618       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1619       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1620 
1621       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1622       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));
1623       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));
1624     } break;
1625     default:
1626       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1627     }
1628     break;
1629   case DM_POLYTOPE_TRIANGLE:
1630     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));
1631     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1632     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1633     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1634     break;
1635   case DM_POLYTOPE_QUADRILATERAL:
1636     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));
1637     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));
1638     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1639     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1640     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1641     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1642     break;
1643   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1644     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));
1645     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));
1646     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1647     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1648     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1649     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1650     break;
1651   case DM_POLYTOPE_FV_GHOST:
1652     break;
1653   default:
1654     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1655   }
1656   PetscFunctionReturn(0);
1657 }
1658 
1659 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1660 {
1661   DMPolytopeType ct;
1662   PetscReal      centroid[2] = {0., 0.};
1663   PetscMPIInt    rank;
1664   PetscInt       fillColor, v, e, d;
1665 
1666   PetscFunctionBegin;
1667   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1668   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1669   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1670   switch (ct) {
1671   case DM_POLYTOPE_TRIANGLE: {
1672     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1673 
1674     for (v = 0; v < 3; ++v) {
1675       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1676       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1677     }
1678     for (e = 0; e < 3; ++e) {
1679       refCoords[0] = refVertices[e * 2 + 0];
1680       refCoords[1] = refVertices[e * 2 + 1];
1681       for (d = 1; d <= edgeDiv; ++d) {
1682         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1683         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1684       }
1685       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1686       for (d = 0; d < edgeDiv; ++d) {
1687         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));
1688         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1689       }
1690     }
1691   } break;
1692   default:
1693     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1694   }
1695   PetscFunctionReturn(0);
1696 }
1697 
1698 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1699 {
1700   PetscDraw          draw;
1701   DM                 cdm;
1702   PetscSection       coordSection;
1703   Vec                coordinates;
1704   const PetscScalar *coords;
1705   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1706   PetscReal         *refCoords, *edgeCoords;
1707   PetscBool          isnull, drawAffine = PETSC_TRUE;
1708   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1709 
1710   PetscFunctionBegin;
1711   PetscCall(DMGetCoordinateDim(dm, &dim));
1712   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1713   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1714   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1715   PetscCall(DMGetCoordinateDM(dm, &cdm));
1716   PetscCall(DMGetLocalSection(cdm, &coordSection));
1717   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1718   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1719   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1720 
1721   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1722   PetscCall(PetscDrawIsNull(draw, &isnull));
1723   if (isnull) PetscFunctionReturn(0);
1724   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1725 
1726   PetscCall(VecGetLocalSize(coordinates, &N));
1727   PetscCall(VecGetArrayRead(coordinates, &coords));
1728   for (c = 0; c < N; c += dim) {
1729     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1730     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1731     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1732     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1733   }
1734   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1735   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1736   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1737   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1738   PetscCall(PetscDrawClear(draw));
1739 
1740   for (c = cStart; c < cEnd; ++c) {
1741     PetscScalar *coords = NULL;
1742     PetscInt     numCoords;
1743 
1744     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1745     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1746     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1747     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1748   }
1749   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1750   PetscCall(PetscDrawFlush(draw));
1751   PetscCall(PetscDrawPause(draw));
1752   PetscCall(PetscDrawSave(draw));
1753   PetscFunctionReturn(0);
1754 }
1755 
1756 #if defined(PETSC_HAVE_EXODUSII)
1757   #include <exodusII.h>
1758   #include <petscviewerexodusii.h>
1759 #endif
1760 
1761 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1762 {
1763   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1764   char      name[PETSC_MAX_PATH_LEN];
1765 
1766   PetscFunctionBegin;
1767   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1768   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1769   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1770   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1771   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1772   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1773   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1774   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1775   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1776   if (iascii) {
1777     PetscViewerFormat format;
1778     PetscCall(PetscViewerGetFormat(viewer, &format));
1779     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1780     else PetscCall(DMPlexView_Ascii(dm, viewer));
1781   } else if (ishdf5) {
1782 #if defined(PETSC_HAVE_HDF5)
1783     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1784 #else
1785     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1786 #endif
1787   } else if (isvtk) {
1788     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1789   } else if (isdraw) {
1790     PetscCall(DMPlexView_Draw(dm, viewer));
1791   } else if (isglvis) {
1792     PetscCall(DMPlexView_GLVis(dm, viewer));
1793 #if defined(PETSC_HAVE_EXODUSII)
1794   } else if (isexodus) {
1795     /*
1796       exodusII requires that all sets be part of exactly one cell set.
1797       If the dm does not have a "Cell Sets" label defined, we create one
1798       with ID 1, containig all cells.
1799       Note that if the Cell Sets label is defined but does not cover all cells,
1800       we may still have a problem. This should probably be checked here or in the viewer;
1801     */
1802     PetscInt numCS;
1803     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1804     if (!numCS) {
1805       PetscInt cStart, cEnd, c;
1806       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1807       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1808       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1809     }
1810     PetscCall(DMView_PlexExodusII(dm, viewer));
1811 #endif
1812 #if defined(PETSC_HAVE_CGNS)
1813   } else if (iscgns) {
1814     PetscCall(DMView_PlexCGNS(dm, viewer));
1815 #endif
1816   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1817   /* Optionally view the partition */
1818   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1819   if (flg) {
1820     Vec ranks;
1821     PetscCall(DMPlexCreateRankField(dm, &ranks));
1822     PetscCall(VecView(ranks, viewer));
1823     PetscCall(VecDestroy(&ranks));
1824   }
1825   /* Optionally view a label */
1826   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1827   if (flg) {
1828     DMLabel label;
1829     Vec     val;
1830 
1831     PetscCall(DMGetLabel(dm, name, &label));
1832     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1833     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1834     PetscCall(VecView(val, viewer));
1835     PetscCall(VecDestroy(&val));
1836   }
1837   PetscFunctionReturn(0);
1838 }
1839 
1840 /*@
1841   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1842 
1843   Collective on dm
1844 
1845   Input Parameters:
1846 + dm     - The `DM` whose topology is to be saved
1847 - viewer - The `PetscViewer` to save it in
1848 
1849   Level: advanced
1850 
1851 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1852 @*/
1853 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1854 {
1855   PetscBool ishdf5;
1856 
1857   PetscFunctionBegin;
1858   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1859   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1860   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1861   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1862   if (ishdf5) {
1863 #if defined(PETSC_HAVE_HDF5)
1864     PetscViewerFormat format;
1865     PetscCall(PetscViewerGetFormat(viewer, &format));
1866     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1867       IS globalPointNumbering;
1868 
1869       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1870       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1871       PetscCall(ISDestroy(&globalPointNumbering));
1872     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1873 #else
1874     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1875 #endif
1876   }
1877   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1878   PetscFunctionReturn(0);
1879 }
1880 
1881 /*@
1882   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1883 
1884   Collective on dm
1885 
1886   Input Parameters:
1887 + dm     - The `DM` whose coordinates are to be saved
1888 - viewer - The `PetscViewer` for saving
1889 
1890   Level: advanced
1891 
1892 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1893 @*/
1894 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1895 {
1896   PetscBool ishdf5;
1897 
1898   PetscFunctionBegin;
1899   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1900   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1901   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1902   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1903   if (ishdf5) {
1904 #if defined(PETSC_HAVE_HDF5)
1905     PetscViewerFormat format;
1906     PetscCall(PetscViewerGetFormat(viewer, &format));
1907     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1908       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1909     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1910 #else
1911     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1912 #endif
1913   }
1914   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1915   PetscFunctionReturn(0);
1916 }
1917 
1918 /*@
1919   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1920 
1921   Collective on dm
1922 
1923   Input Parameters:
1924 + dm     - The `DM` whose labels are to be saved
1925 - viewer - The `PetscViewer` for saving
1926 
1927   Level: advanced
1928 
1929 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1930 @*/
1931 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1932 {
1933   PetscBool ishdf5;
1934 
1935   PetscFunctionBegin;
1936   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1937   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1938   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1939   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1940   if (ishdf5) {
1941 #if defined(PETSC_HAVE_HDF5)
1942     IS                globalPointNumbering;
1943     PetscViewerFormat format;
1944 
1945     PetscCall(PetscViewerGetFormat(viewer, &format));
1946     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1947       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1948       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1949       PetscCall(ISDestroy(&globalPointNumbering));
1950     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1951 #else
1952     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1953 #endif
1954   }
1955   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1956   PetscFunctionReturn(0);
1957 }
1958 
1959 /*@
1960   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1961 
1962   Collective on dm
1963 
1964   Input Parameters:
1965 + dm         - The `DM` that contains the topology on which the section to be saved is defined
1966 . viewer     - The `PetscViewer` for saving
1967 - sectiondm  - The `DM` that contains the section to be saved
1968 
1969   Level: advanced
1970 
1971   Notes:
1972   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.
1973 
1974   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.
1975 
1976 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1977 @*/
1978 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1979 {
1980   PetscBool ishdf5;
1981 
1982   PetscFunctionBegin;
1983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1984   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1985   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1986   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1987   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1988   if (ishdf5) {
1989 #if defined(PETSC_HAVE_HDF5)
1990     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1991 #else
1992     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1993 #endif
1994   }
1995   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1996   PetscFunctionReturn(0);
1997 }
1998 
1999 /*@
2000   DMPlexGlobalVectorView - Saves a global vector
2001 
2002   Collective on dm
2003 
2004   Input Parameters:
2005 + dm        - The `DM` that represents the topology
2006 . viewer    - The `PetscViewer` to save data with
2007 . sectiondm - The `DM` that contains the global section on which vec is defined
2008 - vec       - The global vector to be saved
2009 
2010   Level: advanced
2011 
2012   Notes:
2013   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.
2014 
2015   Typical calling sequence:
2016 .vb
2017        DMCreate(PETSC_COMM_WORLD, &dm);
2018        DMSetType(dm, DMPLEX);
2019        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2020        DMClone(dm, &sectiondm);
2021        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2022        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2023        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2024        PetscSectionSetChart(section, pStart, pEnd);
2025        PetscSectionSetUp(section);
2026        DMSetLocalSection(sectiondm, section);
2027        PetscSectionDestroy(&section);
2028        DMGetGlobalVector(sectiondm, &vec);
2029        PetscObjectSetName((PetscObject)vec, "vec_name");
2030        DMPlexTopologyView(dm, viewer);
2031        DMPlexSectionView(dm, viewer, sectiondm);
2032        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2033        DMRestoreGlobalVector(sectiondm, &vec);
2034        DMDestroy(&sectiondm);
2035        DMDestroy(&dm);
2036 .ve
2037 
2038 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2039 @*/
2040 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2041 {
2042   PetscBool ishdf5;
2043 
2044   PetscFunctionBegin;
2045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2046   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2047   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2048   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2049   /* Check consistency */
2050   {
2051     PetscSection section;
2052     PetscBool    includesConstraints;
2053     PetscInt     m, m1;
2054 
2055     PetscCall(VecGetLocalSize(vec, &m1));
2056     PetscCall(DMGetGlobalSection(sectiondm, &section));
2057     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2058     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2059     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2060     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2061   }
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2063   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2064   if (ishdf5) {
2065 #if defined(PETSC_HAVE_HDF5)
2066     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2067 #else
2068     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2069 #endif
2070   }
2071   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2072   PetscFunctionReturn(0);
2073 }
2074 
2075 /*@
2076   DMPlexLocalVectorView - Saves a local vector
2077 
2078   Collective on dm
2079 
2080   Input Parameters:
2081 + dm        - The `DM` that represents the topology
2082 . viewer    - The `PetscViewer` to save data with
2083 . sectiondm - The `DM` that contains the local section on which vec is defined; may be the same as dm
2084 - vec       - The local vector to be saved
2085 
2086   Level: advanced
2087 
2088   Note:
2089   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.
2090 
2091   Typical calling sequence:
2092 .vb
2093        DMCreate(PETSC_COMM_WORLD, &dm);
2094        DMSetType(dm, DMPLEX);
2095        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2096        DMClone(dm, &sectiondm);
2097        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2098        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2099        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2100        PetscSectionSetChart(section, pStart, pEnd);
2101        PetscSectionSetUp(section);
2102        DMSetLocalSection(sectiondm, section);
2103        DMGetLocalVector(sectiondm, &vec);
2104        PetscObjectSetName((PetscObject)vec, "vec_name");
2105        DMPlexTopologyView(dm, viewer);
2106        DMPlexSectionView(dm, viewer, sectiondm);
2107        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2108        DMRestoreLocalVector(sectiondm, &vec);
2109        DMDestroy(&sectiondm);
2110        DMDestroy(&dm);
2111 .ve
2112 
2113 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2114 @*/
2115 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2116 {
2117   PetscBool ishdf5;
2118 
2119   PetscFunctionBegin;
2120   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2121   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2122   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2123   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2124   /* Check consistency */
2125   {
2126     PetscSection section;
2127     PetscBool    includesConstraints;
2128     PetscInt     m, m1;
2129 
2130     PetscCall(VecGetLocalSize(vec, &m1));
2131     PetscCall(DMGetLocalSection(sectiondm, &section));
2132     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2133     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2134     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2135     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2136   }
2137   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2138   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2139   if (ishdf5) {
2140 #if defined(PETSC_HAVE_HDF5)
2141     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2142 #else
2143     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2144 #endif
2145   }
2146   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2147   PetscFunctionReturn(0);
2148 }
2149 
2150 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2151 {
2152   PetscBool ishdf5;
2153 
2154   PetscFunctionBegin;
2155   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2156   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2157   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2158   if (ishdf5) {
2159 #if defined(PETSC_HAVE_HDF5)
2160     PetscViewerFormat format;
2161     PetscCall(PetscViewerGetFormat(viewer, &format));
2162     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2163       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2164     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2165       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2166     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2167     PetscFunctionReturn(0);
2168 #else
2169     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2170 #endif
2171   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2172 }
2173 
2174 /*@
2175   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2176 
2177   Collective on dm
2178 
2179   Input Parameters:
2180 + dm                - The `DM` into which the topology is loaded
2181 - viewer            - The `PetscViewer` for the saved topology
2182 
2183   Output Parameters:
2184 . 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
2185 
2186   Level: advanced
2187 
2188 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2189           `PetscViewer`, `PetscSF`
2190 @*/
2191 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2192 {
2193   PetscBool ishdf5;
2194 
2195   PetscFunctionBegin;
2196   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2197   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2198   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2199   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2200   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2201   if (ishdf5) {
2202 #if defined(PETSC_HAVE_HDF5)
2203     PetscViewerFormat format;
2204     PetscCall(PetscViewerGetFormat(viewer, &format));
2205     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2206       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2207     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2208 #else
2209     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2210 #endif
2211   }
2212   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2213   PetscFunctionReturn(0);
2214 }
2215 
2216 /*@
2217   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2218 
2219   Collective on dm
2220 
2221   Input Parameters:
2222 + dm     - The `DM` into which the coordinates are loaded
2223 . viewer - The `PetscViewer` for the saved coordinates
2224 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2225 
2226   Level: advanced
2227 
2228 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2229           `PetscSF`, `PetscViewer`
2230 @*/
2231 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2232 {
2233   PetscBool ishdf5;
2234 
2235   PetscFunctionBegin;
2236   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2237   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2238   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2239   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2240   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2241   if (ishdf5) {
2242 #if defined(PETSC_HAVE_HDF5)
2243     PetscViewerFormat format;
2244     PetscCall(PetscViewerGetFormat(viewer, &format));
2245     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2246       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2247     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2248 #else
2249     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2250 #endif
2251   }
2252   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2253   PetscFunctionReturn(0);
2254 }
2255 
2256 /*@
2257   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2258 
2259   Collective on dm
2260 
2261   Input Parameters:
2262 + dm     - The `DM` into which the labels are loaded
2263 . viewer - The `PetscViewer` for the saved labels
2264 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2265 
2266   Level: advanced
2267 
2268   Note:
2269   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2270 
2271 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2272           `PetscSF`, `PetscViewer`
2273 @*/
2274 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2275 {
2276   PetscBool ishdf5;
2277 
2278   PetscFunctionBegin;
2279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2280   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2281   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2282   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2283   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2284   if (ishdf5) {
2285 #if defined(PETSC_HAVE_HDF5)
2286     PetscViewerFormat format;
2287 
2288     PetscCall(PetscViewerGetFormat(viewer, &format));
2289     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2290       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2291     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2292 #else
2293     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2294 #endif
2295   }
2296   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2297   PetscFunctionReturn(0);
2298 }
2299 
2300 /*@
2301   DMPlexSectionLoad - Loads section into a `DMPLEX`
2302 
2303   Collective on dm
2304 
2305   Input Parameters:
2306 + dm          - The `DM` that represents the topology
2307 . viewer      - The `PetscViewer` that represents the on-disk section (sectionA)
2308 . sectiondm   - The `DM` into which the on-disk section (sectionA) is migrated
2309 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2310 
2311   Output Parameters
2312 + 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)
2313 - 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)
2314 
2315   Level: advanced
2316 
2317   Notes:
2318   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.
2319 
2320   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.
2321 
2322   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.
2323 
2324   Example using 2 processes:
2325 .vb
2326   NX (number of points on dm): 4
2327   sectionA                   : the on-disk section
2328   vecA                       : a vector associated with sectionA
2329   sectionB                   : sectiondm's local section constructed in this function
2330   vecB (local)               : a vector associated with sectiondm's local section
2331   vecB (global)              : a vector associated with sectiondm's global section
2332 
2333                                      rank 0    rank 1
2334   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2335   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2336   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2337   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2338   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2339   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2340   sectionB->atlasDof             :     1 0 1 | 1 3
2341   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2342   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2343   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2344 .ve
2345   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2346 
2347 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2348 @*/
2349 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2350 {
2351   PetscBool ishdf5;
2352 
2353   PetscFunctionBegin;
2354   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2355   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2356   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2357   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2358   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2359   if (localDofSF) PetscValidPointer(localDofSF, 6);
2360   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2361   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2362   if (ishdf5) {
2363 #if defined(PETSC_HAVE_HDF5)
2364     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2365 #else
2366     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2367 #endif
2368   }
2369   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2370   PetscFunctionReturn(0);
2371 }
2372 
2373 /*@
2374   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2375 
2376   Collective on dm
2377 
2378   Input Parameters:
2379 + dm        - The `DM` that represents the topology
2380 . viewer    - The `PetscViewer` that represents the on-disk vector data
2381 . sectiondm - The `DM` that contains the global section on which vec is defined
2382 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2383 - vec       - The global vector to set values of
2384 
2385   Level: advanced
2386 
2387   Notes:
2388   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.
2389 
2390   Typical calling sequence:
2391 .vb
2392        DMCreate(PETSC_COMM_WORLD, &dm);
2393        DMSetType(dm, DMPLEX);
2394        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2395        DMPlexTopologyLoad(dm, viewer, &sfX);
2396        DMClone(dm, &sectiondm);
2397        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2398        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2399        DMGetGlobalVector(sectiondm, &vec);
2400        PetscObjectSetName((PetscObject)vec, "vec_name");
2401        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2402        DMRestoreGlobalVector(sectiondm, &vec);
2403        PetscSFDestroy(&gsf);
2404        PetscSFDestroy(&sfX);
2405        DMDestroy(&sectiondm);
2406        DMDestroy(&dm);
2407 .ve
2408 
2409 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2410           `PetscSF`, `PetscViewer`
2411 @*/
2412 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2413 {
2414   PetscBool ishdf5;
2415 
2416   PetscFunctionBegin;
2417   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2418   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2419   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2420   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2421   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2422   /* Check consistency */
2423   {
2424     PetscSection section;
2425     PetscBool    includesConstraints;
2426     PetscInt     m, m1;
2427 
2428     PetscCall(VecGetLocalSize(vec, &m1));
2429     PetscCall(DMGetGlobalSection(sectiondm, &section));
2430     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2431     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2432     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2433     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2434   }
2435   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2436   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2437   if (ishdf5) {
2438 #if defined(PETSC_HAVE_HDF5)
2439     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2440 #else
2441     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2442 #endif
2443   }
2444   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2445   PetscFunctionReturn(0);
2446 }
2447 
2448 /*@
2449   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2450 
2451   Collective on dm
2452 
2453   Input Parameters:
2454 + dm        - The `DM` that represents the topology
2455 . viewer    - The `PetscViewer` that represents the on-disk vector data
2456 . sectiondm - The `DM` that contains the local section on which vec is defined
2457 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2458 - vec       - The local vector to set values of
2459 
2460   Level: advanced
2461 
2462   Notes:
2463   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.
2464 
2465   Typical calling sequence:
2466 .vb
2467        DMCreate(PETSC_COMM_WORLD, &dm);
2468        DMSetType(dm, DMPLEX);
2469        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2470        DMPlexTopologyLoad(dm, viewer, &sfX);
2471        DMClone(dm, &sectiondm);
2472        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2473        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2474        DMGetLocalVector(sectiondm, &vec);
2475        PetscObjectSetName((PetscObject)vec, "vec_name");
2476        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2477        DMRestoreLocalVector(sectiondm, &vec);
2478        PetscSFDestroy(&lsf);
2479        PetscSFDestroy(&sfX);
2480        DMDestroy(&sectiondm);
2481        DMDestroy(&dm);
2482 .ve
2483 
2484 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2485           `PetscSF`, `PetscViewer`
2486 @*/
2487 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2488 {
2489   PetscBool ishdf5;
2490 
2491   PetscFunctionBegin;
2492   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2493   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2494   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2495   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2496   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2497   /* Check consistency */
2498   {
2499     PetscSection section;
2500     PetscBool    includesConstraints;
2501     PetscInt     m, m1;
2502 
2503     PetscCall(VecGetLocalSize(vec, &m1));
2504     PetscCall(DMGetLocalSection(sectiondm, &section));
2505     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2506     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2507     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2508     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2509   }
2510   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2511   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2512   if (ishdf5) {
2513 #if defined(PETSC_HAVE_HDF5)
2514     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2515 #else
2516     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2517 #endif
2518   }
2519   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2520   PetscFunctionReturn(0);
2521 }
2522 
2523 PetscErrorCode DMDestroy_Plex(DM dm)
2524 {
2525   DM_Plex *mesh = (DM_Plex *)dm->data;
2526 
2527   PetscFunctionBegin;
2528   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2529   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2530   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2531   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2532   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2533   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2534   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2535   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2536   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2537   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2538   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2539   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2540   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2541   if (--mesh->refct > 0) PetscFunctionReturn(0);
2542   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2543   PetscCall(PetscFree(mesh->cones));
2544   PetscCall(PetscFree(mesh->coneOrientations));
2545   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2546   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2547   PetscCall(PetscFree(mesh->supports));
2548   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2549   PetscCall(PetscFree(mesh->facesTmp));
2550   PetscCall(PetscFree(mesh->tetgenOpts));
2551   PetscCall(PetscFree(mesh->triangleOpts));
2552   PetscCall(PetscFree(mesh->transformType));
2553   PetscCall(PetscFree(mesh->distributionName));
2554   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2555   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2556   PetscCall(ISDestroy(&mesh->subpointIS));
2557   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2558   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2559   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2560   PetscCall(ISDestroy(&mesh->anchorIS));
2561   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2562   PetscCall(PetscFree(mesh->parents));
2563   PetscCall(PetscFree(mesh->childIDs));
2564   PetscCall(PetscSectionDestroy(&mesh->childSection));
2565   PetscCall(PetscFree(mesh->children));
2566   PetscCall(DMDestroy(&mesh->referenceTree));
2567   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2568   PetscCall(PetscFree(mesh->neighbors));
2569   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2570   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2571   PetscCall(PetscFree(mesh));
2572   PetscFunctionReturn(0);
2573 }
2574 
2575 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2576 {
2577   PetscSection           sectionGlobal;
2578   PetscInt               bs = -1, mbs;
2579   PetscInt               localSize, localStart = 0;
2580   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2581   MatType                mtype;
2582   ISLocalToGlobalMapping ltog;
2583 
2584   PetscFunctionBegin;
2585   PetscCall(MatInitializePackage());
2586   mtype = dm->mattype;
2587   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2588   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2589   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2590   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2591   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2592   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2593   PetscCall(MatSetType(*J, mtype));
2594   PetscCall(MatSetFromOptions(*J));
2595   PetscCall(MatGetBlockSize(*J, &mbs));
2596   if (mbs > 1) bs = mbs;
2597   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2598   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2599   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2600   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2601   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2602   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2603   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2604   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2605   if (!isShell) {
2606     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2607     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2608     PetscInt  pStart, pEnd, p, dof, cdof;
2609 
2610     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2611 
2612     PetscCall(PetscCalloc1(localSize, &pblocks));
2613     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2614     for (p = pStart; p < pEnd; ++p) {
2615       PetscInt bdof, offset;
2616 
2617       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2618       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2619       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2620       for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2621       dof  = dof < 0 ? -(dof + 1) : dof;
2622       bdof = cdof && (dof - cdof) ? 1 : dof;
2623       if (dof) {
2624         if (bs < 0) {
2625           bs = bdof;
2626         } else if (bs != bdof) {
2627           bs = 1;
2628         }
2629       }
2630     }
2631     /* Must have same blocksize on all procs (some might have no points) */
2632     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2633     bsLocal[1] = bs;
2634     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2635     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2636     else bs = bsMinMax[0];
2637     bs = PetscMax(1, bs);
2638     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2639     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2640       PetscCall(MatSetBlockSize(*J, bs));
2641       PetscCall(MatSetUp(*J));
2642     } else {
2643       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2644       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2645       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2646     }
2647     { // Consolidate blocks
2648       PetscInt nblocks = 0;
2649       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2650         if (pblocks[i] == 0) continue;
2651         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2652         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]);
2653       }
2654       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2655     }
2656     PetscCall(PetscFree(pblocks));
2657   }
2658   PetscCall(MatSetDM(*J, dm));
2659   PetscFunctionReturn(0);
2660 }
2661 
2662 /*@
2663   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2664 
2665   Not Collective
2666 
2667   Input Parameter:
2668 . mesh - The `DMPLEX`
2669 
2670   Output Parameters:
2671 . subsection - The subdomain section
2672 
2673   Level: developer
2674 
2675 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `PetscSection`
2676 @*/
2677 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2678 {
2679   DM_Plex *mesh = (DM_Plex *)dm->data;
2680 
2681   PetscFunctionBegin;
2682   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2683   if (!mesh->subdomainSection) {
2684     PetscSection section;
2685     PetscSF      sf;
2686 
2687     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2688     PetscCall(DMGetLocalSection(dm, &section));
2689     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2690     PetscCall(PetscSFDestroy(&sf));
2691   }
2692   *subsection = mesh->subdomainSection;
2693   PetscFunctionReturn(0);
2694 }
2695 
2696 /*@
2697   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2698 
2699   Not Collective
2700 
2701   Input Parameter:
2702 . mesh - The `DMPLEX`
2703 
2704   Output Parameters:
2705 + pStart - The first mesh point
2706 - pEnd   - The upper bound for mesh points
2707 
2708   Level: beginner
2709 
2710 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2711 @*/
2712 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2713 {
2714   DM_Plex *mesh = (DM_Plex *)dm->data;
2715 
2716   PetscFunctionBegin;
2717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2718   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2719   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2720   PetscFunctionReturn(0);
2721 }
2722 
2723 /*@
2724   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2725 
2726   Not Collective
2727 
2728   Input Parameters:
2729 + mesh - The `DMPLEX`
2730 . pStart - The first mesh point
2731 - pEnd   - The upper bound for mesh points
2732 
2733   Level: beginner
2734 
2735 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2736 @*/
2737 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2738 {
2739   DM_Plex *mesh = (DM_Plex *)dm->data;
2740 
2741   PetscFunctionBegin;
2742   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2743   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2744   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2745   PetscFunctionReturn(0);
2746 }
2747 
2748 /*@
2749   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2750 
2751   Not Collective
2752 
2753   Input Parameters:
2754 + mesh - The `DMPLEX`
2755 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2756 
2757   Output Parameter:
2758 . size - The cone size for point p
2759 
2760   Level: beginner
2761 
2762 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2763 @*/
2764 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2765 {
2766   DM_Plex *mesh = (DM_Plex *)dm->data;
2767 
2768   PetscFunctionBegin;
2769   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2770   PetscValidIntPointer(size, 3);
2771   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2772   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2773   PetscFunctionReturn(0);
2774 }
2775 
2776 /*@
2777   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2778 
2779   Not Collective
2780 
2781   Input Parameters:
2782 + mesh - The `DMPLEX`
2783 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
2784 - size - The cone size for point p
2785 
2786   Level: beginner
2787 
2788   Note:
2789   This should be called after `DMPlexSetChart()`.
2790 
2791 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2792 @*/
2793 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2794 {
2795   DM_Plex *mesh = (DM_Plex *)dm->data;
2796 
2797   PetscFunctionBegin;
2798   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2799   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2800   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2801   PetscFunctionReturn(0);
2802 }
2803 
2804 /*@C
2805   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2806 
2807   Not Collective
2808 
2809   Input Parameters:
2810 + dm - The `DMPLEX`
2811 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
2812 
2813   Output Parameter:
2814 . cone - An array of points which are on the in-edges for point p
2815 
2816   Level: beginner
2817 
2818   Fortran Note:
2819   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2820   `DMPlexRestoreCone()` is not needed/available in C.
2821 
2822 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2823 @*/
2824 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2825 {
2826   DM_Plex *mesh = (DM_Plex *)dm->data;
2827   PetscInt off;
2828 
2829   PetscFunctionBegin;
2830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2831   PetscValidPointer(cone, 3);
2832   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2833   *cone = &mesh->cones[off];
2834   PetscFunctionReturn(0);
2835 }
2836 
2837 /*@C
2838   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2839 
2840   Not Collective
2841 
2842   Input Parameters:
2843 + dm - The `DMPLEX`
2844 - p - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2845 
2846   Output Parameters:
2847 + pConesSection - `PetscSection` describing the layout of pCones
2848 - pCones - An array of points which are on the in-edges for the point set p
2849 
2850   Level: intermediate
2851 
2852 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2853 @*/
2854 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2855 {
2856   PetscSection cs, newcs;
2857   PetscInt    *cones;
2858   PetscInt    *newarr = NULL;
2859   PetscInt     n;
2860 
2861   PetscFunctionBegin;
2862   PetscCall(DMPlexGetCones(dm, &cones));
2863   PetscCall(DMPlexGetConeSection(dm, &cs));
2864   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2865   if (pConesSection) *pConesSection = newcs;
2866   if (pCones) {
2867     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2868     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2869   }
2870   PetscFunctionReturn(0);
2871 }
2872 
2873 /*@
2874   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2875 
2876   Not Collective
2877 
2878   Input Parameters:
2879 + dm - The `DMPLEX`
2880 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2881 
2882   Output Parameter:
2883 . expandedPoints - An array of vertices recursively expanded from input points
2884 
2885   Level: advanced
2886 
2887   Notes:
2888   Like `DMPlexGetConeRecursive()` but returns only the 0-depth IS (i.e. vertices only) and no sections.
2889 
2890   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2891 
2892 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2893           `DMPlexGetDepth()`, `IS`
2894 @*/
2895 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2896 {
2897   IS      *expandedPointsAll;
2898   PetscInt depth;
2899 
2900   PetscFunctionBegin;
2901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2902   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2903   PetscValidPointer(expandedPoints, 3);
2904   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2905   *expandedPoints = expandedPointsAll[0];
2906   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2907   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2908   PetscFunctionReturn(0);
2909 }
2910 
2911 /*@
2912   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).
2913 
2914   Not Collective
2915 
2916   Input Parameters:
2917 + dm - The `DMPLEX`
2918 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2919 
2920   Output Parameters:
2921 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2922 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2923 - sections - (optional) An array of sections which describe mappings from points to their cone points
2924 
2925   Level: advanced
2926 
2927   Notes:
2928   Like `DMPlexGetConeTuple()` but recursive.
2929 
2930   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.
2931   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2932 
2933   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:
2934   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2935   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2936 
2937 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2938           `DMPlexGetDepth()`, `PetscSection`, `IS`
2939 @*/
2940 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2941 {
2942   const PetscInt *arr0 = NULL, *cone = NULL;
2943   PetscInt       *arr = NULL, *newarr = NULL;
2944   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2945   IS             *expandedPoints_;
2946   PetscSection   *sections_;
2947 
2948   PetscFunctionBegin;
2949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2950   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2951   if (depth) PetscValidIntPointer(depth, 3);
2952   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2953   if (sections) PetscValidPointer(sections, 5);
2954   PetscCall(ISGetLocalSize(points, &n));
2955   PetscCall(ISGetIndices(points, &arr0));
2956   PetscCall(DMPlexGetDepth(dm, &depth_));
2957   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2958   PetscCall(PetscCalloc1(depth_, &sections_));
2959   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2960   for (d = depth_ - 1; d >= 0; d--) {
2961     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2962     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2963     for (i = 0; i < n; i++) {
2964       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2965       if (arr[i] >= start && arr[i] < end) {
2966         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2967         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2968       } else {
2969         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2970       }
2971     }
2972     PetscCall(PetscSectionSetUp(sections_[d]));
2973     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2974     PetscCall(PetscMalloc1(newn, &newarr));
2975     for (i = 0; i < n; i++) {
2976       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2977       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2978       if (cn > 1) {
2979         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2980         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2981       } else {
2982         newarr[co] = arr[i];
2983       }
2984     }
2985     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2986     arr = newarr;
2987     n   = newn;
2988   }
2989   PetscCall(ISRestoreIndices(points, &arr0));
2990   *depth = depth_;
2991   if (expandedPoints) *expandedPoints = expandedPoints_;
2992   else {
2993     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2994     PetscCall(PetscFree(expandedPoints_));
2995   }
2996   if (sections) *sections = sections_;
2997   else {
2998     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2999     PetscCall(PetscFree(sections_));
3000   }
3001   PetscFunctionReturn(0);
3002 }
3003 
3004 /*@
3005   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3006 
3007   Not Collective
3008 
3009   Input Parameters:
3010 + dm - The `DMPLEX`
3011 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3012 
3013   Output Parameters:
3014 + depth - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3015 . expandedPoints - (optional) An array of recursively expanded cones
3016 - sections - (optional) An array of sections which describe mappings from points to their cone points
3017 
3018   Level: advanced
3019 
3020   Note:
3021   See `DMPlexGetConeRecursive()`
3022 
3023 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3024           `DMPlexGetDepth()`, `IS`, `PetscSection`
3025 @*/
3026 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3027 {
3028   PetscInt d, depth_;
3029 
3030   PetscFunctionBegin;
3031   PetscCall(DMPlexGetDepth(dm, &depth_));
3032   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3033   if (depth) *depth = 0;
3034   if (expandedPoints) {
3035     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3036     PetscCall(PetscFree(*expandedPoints));
3037   }
3038   if (sections) {
3039     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3040     PetscCall(PetscFree(*sections));
3041   }
3042   PetscFunctionReturn(0);
3043 }
3044 
3045 /*@
3046   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
3047 
3048   Not Collective
3049 
3050   Input Parameters:
3051 + mesh - The `DMPLEX`
3052 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3053 - cone - An array of points which are on the in-edges for point p
3054 
3055   Level: beginner
3056 
3057   Note:
3058   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3059 
3060 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3061 @*/
3062 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3063 {
3064   DM_Plex *mesh = (DM_Plex *)dm->data;
3065   PetscInt pStart, pEnd;
3066   PetscInt dof, off, c;
3067 
3068   PetscFunctionBegin;
3069   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3070   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3071   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3072   if (dof) PetscValidIntPointer(cone, 3);
3073   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3074   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);
3075   for (c = 0; c < dof; ++c) {
3076     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);
3077     mesh->cones[off + c] = cone[c];
3078   }
3079   PetscFunctionReturn(0);
3080 }
3081 
3082 /*@C
3083   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3084 
3085   Not Collective
3086 
3087   Input Parameters:
3088 + mesh - The `DMPLEX`
3089 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3090 
3091   Output Parameter:
3092 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3093                     integer giving the prescription for cone traversal.
3094 
3095   Level: beginner
3096 
3097   Note:
3098   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3099   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3100   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3101   with the identity.
3102 
3103   Fortran Note:
3104   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3105   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3106 
3107 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3108 @*/
3109 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3110 {
3111   DM_Plex *mesh = (DM_Plex *)dm->data;
3112   PetscInt off;
3113 
3114   PetscFunctionBegin;
3115   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3116   if (PetscDefined(USE_DEBUG)) {
3117     PetscInt dof;
3118     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3119     if (dof) PetscValidPointer(coneOrientation, 3);
3120   }
3121   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3122 
3123   *coneOrientation = &mesh->coneOrientations[off];
3124   PetscFunctionReturn(0);
3125 }
3126 
3127 /*@
3128   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3129 
3130   Not Collective
3131 
3132   Input Parameters:
3133 + mesh - The `DMPLEX`
3134 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3135 - coneOrientation - An array of orientations
3136 
3137   Level: beginner
3138 
3139   Notes:
3140   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3141 
3142   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3143 
3144 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3145 @*/
3146 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3147 {
3148   DM_Plex *mesh = (DM_Plex *)dm->data;
3149   PetscInt pStart, pEnd;
3150   PetscInt dof, off, c;
3151 
3152   PetscFunctionBegin;
3153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3154   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3155   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3156   if (dof) PetscValidIntPointer(coneOrientation, 3);
3157   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3158   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);
3159   for (c = 0; c < dof; ++c) {
3160     PetscInt cdof, o = coneOrientation[c];
3161 
3162     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3163     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);
3164     mesh->coneOrientations[off + c] = o;
3165   }
3166   PetscFunctionReturn(0);
3167 }
3168 
3169 /*@
3170   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3171 
3172   Not Collective
3173 
3174   Input Parameters:
3175 + mesh - The `DMPLEX`
3176 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3177 . conePos - The local index in the cone where the point should be put
3178 - conePoint - The mesh point to insert
3179 
3180   Level: beginner
3181 
3182 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3183 @*/
3184 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3185 {
3186   DM_Plex *mesh = (DM_Plex *)dm->data;
3187   PetscInt pStart, pEnd;
3188   PetscInt dof, off;
3189 
3190   PetscFunctionBegin;
3191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3192   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3193   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);
3194   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);
3195   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3196   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3197   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);
3198   mesh->cones[off + conePos] = conePoint;
3199   PetscFunctionReturn(0);
3200 }
3201 
3202 /*@
3203   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3204 
3205   Not Collective
3206 
3207   Input Parameters:
3208 + mesh - The `DMPLEX`
3209 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3210 . conePos - The local index in the cone where the point should be put
3211 - coneOrientation - The point orientation to insert
3212 
3213   Level: beginner
3214 
3215   Note:
3216   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3217 
3218 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3219 @*/
3220 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3221 {
3222   DM_Plex *mesh = (DM_Plex *)dm->data;
3223   PetscInt pStart, pEnd;
3224   PetscInt dof, off;
3225 
3226   PetscFunctionBegin;
3227   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3228   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3229   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);
3230   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3231   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3232   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);
3233   mesh->coneOrientations[off + conePos] = coneOrientation;
3234   PetscFunctionReturn(0);
3235 }
3236 
3237 /*@C
3238   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3239 
3240   Not collective
3241 
3242   Input Parameters:
3243 + dm - The DMPlex
3244 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3245 
3246   Output Parameters:
3247 + cone - An array of points which are on the in-edges for point p
3248 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3249         integer giving the prescription for cone traversal.
3250 
3251   Level: beginner
3252 
3253   Notes:
3254   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3255   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3256   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3257   with the identity.
3258 
3259   Fortran Notes:
3260   Since it returns an array, this routine is only available in Fortran 90, and you must
3261   include petsc.h90 in your code.
3262   You must also call DMPlexRestoreCone() after you finish using the returned array.
3263   DMPlexRestoreCone() is not needed/available in C.
3264 
3265 .seealso: `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3266 @*/
3267 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3268 {
3269   DM_Plex *mesh = (DM_Plex *)dm->data;
3270 
3271   PetscFunctionBegin;
3272   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3273   if (mesh->tr) {
3274     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3275   } else {
3276     PetscInt off;
3277     if (PetscDefined(USE_DEBUG)) {
3278       PetscInt dof;
3279       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3280       if (dof) {
3281         if (cone) PetscValidPointer(cone, 3);
3282         if (ornt) PetscValidPointer(ornt, 4);
3283       }
3284     }
3285     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3286     if (cone) *cone = &mesh->cones[off];
3287     if (ornt) *ornt = &mesh->coneOrientations[off];
3288   }
3289   PetscFunctionReturn(0);
3290 }
3291 
3292 /*@C
3293   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3294 
3295   Not collective
3296 
3297   Input Parameters:
3298 + dm - The DMPlex
3299 . p  - The point, which must lie in the chart set with DMPlexSetChart()
3300 . cone - An array of points which are on the in-edges for point p
3301 - ornt - An array of orientations which are on the in-edges for point p. An orientation is an
3302         integer giving the prescription for cone traversal.
3303 
3304   Level: beginner
3305 
3306   Notes:
3307   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3308   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3309   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3310   with the identity.
3311 
3312   Fortran Notes:
3313   Since it returns an array, this routine is only available in Fortran 90, and you must
3314   include petsc.h90 in your code.
3315   You must also call DMPlexRestoreCone() after you finish using the returned array.
3316   DMPlexRestoreCone() is not needed/available in C.
3317 
3318 .seealso: `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3319 @*/
3320 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3321 {
3322   DM_Plex *mesh = (DM_Plex *)dm->data;
3323 
3324   PetscFunctionBegin;
3325   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3326   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3327   PetscFunctionReturn(0);
3328 }
3329 
3330 /*@
3331   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3332 
3333   Not Collective
3334 
3335   Input Parameters:
3336 + mesh - The `DMPLEX`
3337 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3338 
3339   Output Parameter:
3340 . size - The support size for point p
3341 
3342   Level: beginner
3343 
3344 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3345 @*/
3346 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3347 {
3348   DM_Plex *mesh = (DM_Plex *)dm->data;
3349 
3350   PetscFunctionBegin;
3351   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3352   PetscValidIntPointer(size, 3);
3353   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3354   PetscFunctionReturn(0);
3355 }
3356 
3357 /*@
3358   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3359 
3360   Not Collective
3361 
3362   Input Parameters:
3363 + mesh - The `DMPLEX`
3364 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3365 - size - The support size for point p
3366 
3367   Level: beginner
3368 
3369   Note:
3370   This should be called after DMPlexSetChart().
3371 
3372 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3373 @*/
3374 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3375 {
3376   DM_Plex *mesh = (DM_Plex *)dm->data;
3377 
3378   PetscFunctionBegin;
3379   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3380   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3381   PetscFunctionReturn(0);
3382 }
3383 
3384 /*@C
3385   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3386 
3387   Not Collective
3388 
3389   Input Parameters:
3390 + mesh - The `DMPLEX`
3391 - p - The point, which must lie in the chart set with `DMPlexSetChart()`
3392 
3393   Output Parameter:
3394 . support - An array of points which are on the out-edges for point p
3395 
3396   Level: beginner
3397 
3398   Fortran Note:
3399   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3400   `DMPlexRestoreSupport()` is not needed/available in C.
3401 
3402 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3403 @*/
3404 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3405 {
3406   DM_Plex *mesh = (DM_Plex *)dm->data;
3407   PetscInt off;
3408 
3409   PetscFunctionBegin;
3410   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3411   PetscValidPointer(support, 3);
3412   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3413   *support = &mesh->supports[off];
3414   PetscFunctionReturn(0);
3415 }
3416 
3417 /*@
3418   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3419 
3420   Not Collective
3421 
3422   Input Parameters:
3423 + mesh - The `DMPLEX`
3424 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3425 - support - An array of points which are on the out-edges for point p
3426 
3427   Level: beginner
3428 
3429   Note:
3430   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3431 
3432 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3433 @*/
3434 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3435 {
3436   DM_Plex *mesh = (DM_Plex *)dm->data;
3437   PetscInt pStart, pEnd;
3438   PetscInt dof, off, c;
3439 
3440   PetscFunctionBegin;
3441   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3442   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3443   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3444   if (dof) PetscValidIntPointer(support, 3);
3445   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3446   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);
3447   for (c = 0; c < dof; ++c) {
3448     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);
3449     mesh->supports[off + c] = support[c];
3450   }
3451   PetscFunctionReturn(0);
3452 }
3453 
3454 /*@
3455   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3456 
3457   Not Collective
3458 
3459   Input Parameters:
3460 + mesh - The `DMPLEX`
3461 . p - The point, which must lie in the chart set with `DMPlexSetChart()`
3462 . supportPos - The local index in the cone where the point should be put
3463 - supportPoint - The mesh point to insert
3464 
3465   Level: beginner
3466 
3467 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3468 @*/
3469 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3470 {
3471   DM_Plex *mesh = (DM_Plex *)dm->data;
3472   PetscInt pStart, pEnd;
3473   PetscInt dof, off;
3474 
3475   PetscFunctionBegin;
3476   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3477   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3478   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3479   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3480   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);
3481   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);
3482   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);
3483   mesh->supports[off + supportPos] = supportPoint;
3484   PetscFunctionReturn(0);
3485 }
3486 
3487 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3488 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3489 {
3490   switch (ct) {
3491   case DM_POLYTOPE_SEGMENT:
3492     if (o == -1) return -2;
3493     break;
3494   case DM_POLYTOPE_TRIANGLE:
3495     if (o == -3) return -1;
3496     if (o == -2) return -3;
3497     if (o == -1) return -2;
3498     break;
3499   case DM_POLYTOPE_QUADRILATERAL:
3500     if (o == -4) return -2;
3501     if (o == -3) return -1;
3502     if (o == -2) return -4;
3503     if (o == -1) return -3;
3504     break;
3505   default:
3506     return o;
3507   }
3508   return o;
3509 }
3510 
3511 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3512 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3513 {
3514   switch (ct) {
3515   case DM_POLYTOPE_SEGMENT:
3516     if ((o == -2) || (o == 1)) return -1;
3517     if (o == -1) return 0;
3518     break;
3519   case DM_POLYTOPE_TRIANGLE:
3520     if (o == -3) return -2;
3521     if (o == -2) return -1;
3522     if (o == -1) return -3;
3523     break;
3524   case DM_POLYTOPE_QUADRILATERAL:
3525     if (o == -4) return -2;
3526     if (o == -3) return -1;
3527     if (o == -2) return -4;
3528     if (o == -1) return -3;
3529     break;
3530   default:
3531     return o;
3532   }
3533   return o;
3534 }
3535 
3536 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3537 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3538 {
3539   PetscInt pStart, pEnd, p;
3540 
3541   PetscFunctionBegin;
3542   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3543   for (p = pStart; p < pEnd; ++p) {
3544     const PetscInt *cone, *ornt;
3545     PetscInt        coneSize, c;
3546 
3547     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3548     PetscCall(DMPlexGetCone(dm, p, &cone));
3549     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3550     for (c = 0; c < coneSize; ++c) {
3551       DMPolytopeType ct;
3552       const PetscInt o = ornt[c];
3553 
3554       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3555       switch (ct) {
3556       case DM_POLYTOPE_SEGMENT:
3557         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3558         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3559         break;
3560       case DM_POLYTOPE_TRIANGLE:
3561         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3562         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3563         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3564         break;
3565       case DM_POLYTOPE_QUADRILATERAL:
3566         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3567         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3568         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3569         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3570         break;
3571       default:
3572         break;
3573       }
3574     }
3575   }
3576   PetscFunctionReturn(0);
3577 }
3578 
3579 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3580 {
3581   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3582   PetscInt       *closure;
3583   const PetscInt *tmp = NULL, *tmpO = NULL;
3584   PetscInt        off = 0, tmpSize, t;
3585 
3586   PetscFunctionBeginHot;
3587   if (ornt) {
3588     PetscCall(DMPlexGetCellType(dm, p, &ct));
3589     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3590   }
3591   if (*points) {
3592     closure = *points;
3593   } else {
3594     PetscInt maxConeSize, maxSupportSize;
3595     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3596     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3597   }
3598   if (useCone) {
3599     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3600     PetscCall(DMPlexGetCone(dm, p, &tmp));
3601     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3602   } else {
3603     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3604     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3605   }
3606   if (ct == DM_POLYTOPE_UNKNOWN) {
3607     closure[off++] = p;
3608     closure[off++] = 0;
3609     for (t = 0; t < tmpSize; ++t) {
3610       closure[off++] = tmp[t];
3611       closure[off++] = tmpO ? tmpO[t] : 0;
3612     }
3613   } else {
3614     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3615 
3616     /* We assume that cells with a valid type have faces with a valid type */
3617     closure[off++] = p;
3618     closure[off++] = ornt;
3619     for (t = 0; t < tmpSize; ++t) {
3620       DMPolytopeType ft;
3621 
3622       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3623       closure[off++] = tmp[arr[t]];
3624       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3625     }
3626   }
3627   if (numPoints) *numPoints = tmpSize + 1;
3628   if (points) *points = closure;
3629   PetscFunctionReturn(0);
3630 }
3631 
3632 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3633 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3634 {
3635   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3636   const PetscInt *cone, *ornt;
3637   PetscInt       *pts, *closure = NULL;
3638   DMPolytopeType  ft;
3639   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3640   PetscInt        dim, coneSize, c, d, clSize, cl;
3641 
3642   PetscFunctionBeginHot;
3643   PetscCall(DMGetDimension(dm, &dim));
3644   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3645   PetscCall(DMPlexGetCone(dm, point, &cone));
3646   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3647   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3648   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3649   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3650   maxSize       = PetscMax(coneSeries, supportSeries);
3651   if (*points) {
3652     pts = *points;
3653   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3654   c        = 0;
3655   pts[c++] = point;
3656   pts[c++] = o;
3657   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3658   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3659   for (cl = 0; cl < clSize * 2; cl += 2) {
3660     pts[c++] = closure[cl];
3661     pts[c++] = closure[cl + 1];
3662   }
3663   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), 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(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3669   for (d = 2; d < coneSize; ++d) {
3670     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3671     pts[c++] = cone[arr[d * 2 + 0]];
3672     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3673   }
3674   if (dim >= 3) {
3675     for (d = 2; d < coneSize; ++d) {
3676       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3677       const PetscInt *fcone, *fornt;
3678       PetscInt        fconeSize, fc, i;
3679 
3680       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3681       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3682       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3683       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3684       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3685       for (fc = 0; fc < fconeSize; ++fc) {
3686         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3687         const PetscInt co = farr[fc * 2 + 1];
3688 
3689         for (i = 0; i < c; i += 2)
3690           if (pts[i] == cp) break;
3691         if (i == c) {
3692           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3693           pts[c++] = cp;
3694           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3695         }
3696       }
3697     }
3698   }
3699   *numPoints = c / 2;
3700   *points    = pts;
3701   PetscFunctionReturn(0);
3702 }
3703 
3704 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3705 {
3706   DMPolytopeType ct;
3707   PetscInt      *closure, *fifo;
3708   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3709   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3710   PetscInt       depth, maxSize;
3711 
3712   PetscFunctionBeginHot;
3713   PetscCall(DMPlexGetDepth(dm, &depth));
3714   if (depth == 1) {
3715     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3716     PetscFunctionReturn(0);
3717   }
3718   PetscCall(DMPlexGetCellType(dm, p, &ct));
3719   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3720   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3721     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3722     PetscFunctionReturn(0);
3723   }
3724   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3725   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3726   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3727   maxSize       = PetscMax(coneSeries, supportSeries);
3728   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3729   if (*points) {
3730     closure = *points;
3731   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3732   closure[closureSize++] = p;
3733   closure[closureSize++] = ornt;
3734   fifo[fifoSize++]       = p;
3735   fifo[fifoSize++]       = ornt;
3736   fifo[fifoSize++]       = ct;
3737   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3738   while (fifoSize - fifoStart) {
3739     const PetscInt       q    = fifo[fifoStart++];
3740     const PetscInt       o    = fifo[fifoStart++];
3741     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3742     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3743     const PetscInt      *tmp, *tmpO;
3744     PetscInt             tmpSize, t;
3745 
3746     if (PetscDefined(USE_DEBUG)) {
3747       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3748       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);
3749     }
3750     if (useCone) {
3751       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3752       PetscCall(DMPlexGetCone(dm, q, &tmp));
3753       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3754     } else {
3755       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3756       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3757       tmpO = NULL;
3758     }
3759     for (t = 0; t < tmpSize; ++t) {
3760       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3761       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3762       const PetscInt cp = tmp[ip];
3763       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3764       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3765       PetscInt       c;
3766 
3767       /* Check for duplicate */
3768       for (c = 0; c < closureSize; c += 2) {
3769         if (closure[c] == cp) break;
3770       }
3771       if (c == closureSize) {
3772         closure[closureSize++] = cp;
3773         closure[closureSize++] = co;
3774         fifo[fifoSize++]       = cp;
3775         fifo[fifoSize++]       = co;
3776         fifo[fifoSize++]       = ct;
3777       }
3778     }
3779   }
3780   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3781   if (numPoints) *numPoints = closureSize / 2;
3782   if (points) *points = closure;
3783   PetscFunctionReturn(0);
3784 }
3785 
3786 /*@C
3787   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3788 
3789   Not Collective
3790 
3791   Input Parameters:
3792 + dm      - The `DMPLEX`
3793 . p       - The mesh point
3794 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3795 
3796   Input/Output Parameter:
3797 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3798            if NULL on input, internal storage will be returned, otherwise the provided array is used
3799 
3800   Output Parameter:
3801 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3802 
3803   Level: beginner
3804 
3805   Note:
3806   If using internal storage (points is NULL on input), each call overwrites the last output.
3807 
3808   Fortran Note:
3809   The numPoints argument is not present in the Fortran binding since it is internal to the array.
3810 
3811 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3812 @*/
3813 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3814 {
3815   PetscFunctionBeginHot;
3816   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3817   if (numPoints) PetscValidIntPointer(numPoints, 4);
3818   if (points) PetscValidPointer(points, 5);
3819   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3820   PetscFunctionReturn(0);
3821 }
3822 
3823 /*@C
3824   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3825 
3826   Not Collective
3827 
3828   Input Parameters:
3829 + dm        - The `DMPLEX`
3830 . p         - The mesh point
3831 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3832 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3833 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3834 
3835   Level: beginner
3836 
3837   Note:
3838   If not using internal storage (points is not NULL on input), this call is unnecessary
3839 
3840 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3841 @*/
3842 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3843 {
3844   PetscFunctionBeginHot;
3845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3846   if (numPoints) *numPoints = 0;
3847   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3848   PetscFunctionReturn(0);
3849 }
3850 
3851 /*@
3852   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3853 
3854   Not Collective
3855 
3856   Input Parameter:
3857 . mesh - The `DMPLEX`
3858 
3859   Output Parameters:
3860 + maxConeSize - The maximum number of in-edges
3861 - maxSupportSize - The maximum number of out-edges
3862 
3863   Level: beginner
3864 
3865 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3866 @*/
3867 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3868 {
3869   DM_Plex *mesh = (DM_Plex *)dm->data;
3870 
3871   PetscFunctionBegin;
3872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3873   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3874   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3875   PetscFunctionReturn(0);
3876 }
3877 
3878 PetscErrorCode DMSetUp_Plex(DM dm)
3879 {
3880   DM_Plex *mesh = (DM_Plex *)dm->data;
3881   PetscInt size, maxSupportSize;
3882 
3883   PetscFunctionBegin;
3884   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3885   PetscCall(PetscSectionSetUp(mesh->coneSection));
3886   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3887   PetscCall(PetscMalloc1(size, &mesh->cones));
3888   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3889   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3890   if (maxSupportSize) {
3891     PetscCall(PetscSectionSetUp(mesh->supportSection));
3892     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3893     PetscCall(PetscMalloc1(size, &mesh->supports));
3894   }
3895   PetscFunctionReturn(0);
3896 }
3897 
3898 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3899 {
3900   PetscFunctionBegin;
3901   if (subdm) PetscCall(DMClone(dm, subdm));
3902   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3903   if (subdm) (*subdm)->useNatural = dm->useNatural;
3904   if (dm->useNatural && dm->sfMigration) {
3905     PetscSF sfNatural;
3906 
3907     (*subdm)->sfMigration = dm->sfMigration;
3908     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3909     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3910     (*subdm)->sfNatural = sfNatural;
3911   }
3912   PetscFunctionReturn(0);
3913 }
3914 
3915 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3916 {
3917   PetscInt i = 0;
3918 
3919   PetscFunctionBegin;
3920   PetscCall(DMClone(dms[0], superdm));
3921   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3922   (*superdm)->useNatural = PETSC_FALSE;
3923   for (i = 0; i < len; i++) {
3924     if (dms[i]->useNatural && dms[i]->sfMigration) {
3925       PetscSF sfNatural;
3926 
3927       (*superdm)->sfMigration = dms[i]->sfMigration;
3928       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3929       (*superdm)->useNatural = PETSC_TRUE;
3930       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3931       (*superdm)->sfNatural = sfNatural;
3932       break;
3933     }
3934   }
3935   PetscFunctionReturn(0);
3936 }
3937 
3938 /*@
3939   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3940 
3941   Not Collective
3942 
3943   Input Parameter:
3944 . mesh - The `DMPLEX`
3945 
3946   Level: beginner
3947 
3948   Note:
3949   This should be called after all calls to `DMPlexSetCone()`
3950 
3951 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3952 @*/
3953 PetscErrorCode DMPlexSymmetrize(DM dm)
3954 {
3955   DM_Plex  *mesh = (DM_Plex *)dm->data;
3956   PetscInt *offsets;
3957   PetscInt  supportSize;
3958   PetscInt  pStart, pEnd, p;
3959 
3960   PetscFunctionBegin;
3961   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3962   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3963   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3964   /* Calculate support sizes */
3965   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3966   for (p = pStart; p < pEnd; ++p) {
3967     PetscInt dof, off, c;
3968 
3969     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3970     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3971     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3972   }
3973   PetscCall(PetscSectionSetUp(mesh->supportSection));
3974   /* Calculate supports */
3975   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3976   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3977   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3978   for (p = pStart; p < pEnd; ++p) {
3979     PetscInt dof, off, c;
3980 
3981     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3982     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3983     for (c = off; c < off + dof; ++c) {
3984       const PetscInt q = mesh->cones[c];
3985       PetscInt       offS;
3986 
3987       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3988 
3989       mesh->supports[offS + offsets[q]] = p;
3990       ++offsets[q];
3991     }
3992   }
3993   PetscCall(PetscFree(offsets));
3994   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
3995   PetscFunctionReturn(0);
3996 }
3997 
3998 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
3999 {
4000   IS stratumIS;
4001 
4002   PetscFunctionBegin;
4003   if (pStart >= pEnd) PetscFunctionReturn(0);
4004   if (PetscDefined(USE_DEBUG)) {
4005     PetscInt  qStart, qEnd, numLevels, level;
4006     PetscBool overlap = PETSC_FALSE;
4007     PetscCall(DMLabelGetNumValues(label, &numLevels));
4008     for (level = 0; level < numLevels; level++) {
4009       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4010       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4011         overlap = PETSC_TRUE;
4012         break;
4013       }
4014     }
4015     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);
4016   }
4017   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4018   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4019   PetscCall(ISDestroy(&stratumIS));
4020   PetscFunctionReturn(0);
4021 }
4022 
4023 /*@
4024   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4025   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
4026   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
4027   the DAG.
4028 
4029   Collective on dm
4030 
4031   Input Parameter:
4032 . mesh - The `DMPLEX`
4033 
4034   Level: beginner
4035 
4036   Notes:
4037   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4038   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4039   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4040   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4041   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4042 
4043   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4044   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4045   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
4046   to interpolate only that one (e0), so that
4047 .vb
4048   cone(c0) = {e0, v2}
4049   cone(e0) = {v0, v1}
4050 .ve
4051   If `DMPlexStratify()` is run on this mesh, it will give depths
4052 .vb
4053    depth 0 = {v0, v1, v2}
4054    depth 1 = {e0, c0}
4055 .ve
4056   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4057 
4058   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4059 
4060 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4061 @*/
4062 PetscErrorCode DMPlexStratify(DM dm)
4063 {
4064   DM_Plex *mesh = (DM_Plex *)dm->data;
4065   DMLabel  label;
4066   PetscInt pStart, pEnd, p;
4067   PetscInt numRoots = 0, numLeaves = 0;
4068 
4069   PetscFunctionBegin;
4070   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4071   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4072 
4073   /* Create depth label */
4074   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4075   PetscCall(DMCreateLabel(dm, "depth"));
4076   PetscCall(DMPlexGetDepthLabel(dm, &label));
4077 
4078   {
4079     /* Initialize roots and count leaves */
4080     PetscInt sMin = PETSC_MAX_INT;
4081     PetscInt sMax = PETSC_MIN_INT;
4082     PetscInt coneSize, supportSize;
4083 
4084     for (p = pStart; p < pEnd; ++p) {
4085       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4086       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4087       if (!coneSize && supportSize) {
4088         sMin = PetscMin(p, sMin);
4089         sMax = PetscMax(p, sMax);
4090         ++numRoots;
4091       } else if (!supportSize && coneSize) {
4092         ++numLeaves;
4093       } else if (!supportSize && !coneSize) {
4094         /* Isolated points */
4095         sMin = PetscMin(p, sMin);
4096         sMax = PetscMax(p, sMax);
4097       }
4098     }
4099     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4100   }
4101 
4102   if (numRoots + numLeaves == (pEnd - pStart)) {
4103     PetscInt sMin = PETSC_MAX_INT;
4104     PetscInt sMax = PETSC_MIN_INT;
4105     PetscInt coneSize, supportSize;
4106 
4107     for (p = pStart; p < pEnd; ++p) {
4108       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4109       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4110       if (!supportSize && coneSize) {
4111         sMin = PetscMin(p, sMin);
4112         sMax = PetscMax(p, sMax);
4113       }
4114     }
4115     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4116   } else {
4117     PetscInt level = 0;
4118     PetscInt qStart, qEnd, q;
4119 
4120     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4121     while (qEnd > qStart) {
4122       PetscInt sMin = PETSC_MAX_INT;
4123       PetscInt sMax = PETSC_MIN_INT;
4124 
4125       for (q = qStart; q < qEnd; ++q) {
4126         const PetscInt *support;
4127         PetscInt        supportSize, s;
4128 
4129         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4130         PetscCall(DMPlexGetSupport(dm, q, &support));
4131         for (s = 0; s < supportSize; ++s) {
4132           sMin = PetscMin(support[s], sMin);
4133           sMax = PetscMax(support[s], sMax);
4134         }
4135       }
4136       PetscCall(DMLabelGetNumValues(label, &level));
4137       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4138       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4139     }
4140   }
4141   { /* just in case there is an empty process */
4142     PetscInt numValues, maxValues = 0, v;
4143 
4144     PetscCall(DMLabelGetNumValues(label, &numValues));
4145     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4146     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4147   }
4148   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4149   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4150   PetscFunctionReturn(0);
4151 }
4152 
4153 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4154 {
4155   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4156   PetscInt       dim, depth, pheight, coneSize;
4157 
4158   PetscFunctionBeginHot;
4159   PetscCall(DMGetDimension(dm, &dim));
4160   PetscCall(DMPlexGetDepth(dm, &depth));
4161   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4162   pheight = depth - pdepth;
4163   if (depth <= 1) {
4164     switch (pdepth) {
4165     case 0:
4166       ct = DM_POLYTOPE_POINT;
4167       break;
4168     case 1:
4169       switch (coneSize) {
4170       case 2:
4171         ct = DM_POLYTOPE_SEGMENT;
4172         break;
4173       case 3:
4174         ct = DM_POLYTOPE_TRIANGLE;
4175         break;
4176       case 4:
4177         switch (dim) {
4178         case 2:
4179           ct = DM_POLYTOPE_QUADRILATERAL;
4180           break;
4181         case 3:
4182           ct = DM_POLYTOPE_TETRAHEDRON;
4183           break;
4184         default:
4185           break;
4186         }
4187         break;
4188       case 5:
4189         ct = DM_POLYTOPE_PYRAMID;
4190         break;
4191       case 6:
4192         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4193         break;
4194       case 8:
4195         ct = DM_POLYTOPE_HEXAHEDRON;
4196         break;
4197       default:
4198         break;
4199       }
4200     }
4201   } else {
4202     if (pdepth == 0) {
4203       ct = DM_POLYTOPE_POINT;
4204     } else if (pheight == 0) {
4205       switch (dim) {
4206       case 1:
4207         switch (coneSize) {
4208         case 2:
4209           ct = DM_POLYTOPE_SEGMENT;
4210           break;
4211         default:
4212           break;
4213         }
4214         break;
4215       case 2:
4216         switch (coneSize) {
4217         case 3:
4218           ct = DM_POLYTOPE_TRIANGLE;
4219           break;
4220         case 4:
4221           ct = DM_POLYTOPE_QUADRILATERAL;
4222           break;
4223         default:
4224           break;
4225         }
4226         break;
4227       case 3:
4228         switch (coneSize) {
4229         case 4:
4230           ct = DM_POLYTOPE_TETRAHEDRON;
4231           break;
4232         case 5: {
4233           const PetscInt *cone;
4234           PetscInt        faceConeSize;
4235 
4236           PetscCall(DMPlexGetCone(dm, p, &cone));
4237           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4238           switch (faceConeSize) {
4239           case 3:
4240             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4241             break;
4242           case 4:
4243             ct = DM_POLYTOPE_PYRAMID;
4244             break;
4245           }
4246         } break;
4247         case 6:
4248           ct = DM_POLYTOPE_HEXAHEDRON;
4249           break;
4250         default:
4251           break;
4252         }
4253         break;
4254       default:
4255         break;
4256       }
4257     } else if (pheight > 0) {
4258       switch (coneSize) {
4259       case 2:
4260         ct = DM_POLYTOPE_SEGMENT;
4261         break;
4262       case 3:
4263         ct = DM_POLYTOPE_TRIANGLE;
4264         break;
4265       case 4:
4266         ct = DM_POLYTOPE_QUADRILATERAL;
4267         break;
4268       default:
4269         break;
4270       }
4271     }
4272   }
4273   *pt = ct;
4274   PetscFunctionReturn(0);
4275 }
4276 
4277 /*@
4278   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4279 
4280   Collective on dm
4281 
4282   Input Parameter:
4283 . mesh - The `DMPLEX`
4284 
4285   Level: developer
4286 
4287   Note:
4288   This function is normally called automatically when a cell type is requested. It creates an
4289   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4290   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4291 
4292   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4293 
4294 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4295 @*/
4296 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4297 {
4298   DM_Plex *mesh;
4299   DMLabel  ctLabel;
4300   PetscInt pStart, pEnd, p;
4301 
4302   PetscFunctionBegin;
4303   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4304   mesh = (DM_Plex *)dm->data;
4305   PetscCall(DMCreateLabel(dm, "celltype"));
4306   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4307   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4308   for (p = pStart; p < pEnd; ++p) {
4309     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4310     PetscInt       pdepth;
4311 
4312     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4313     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4314     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4315     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4316   }
4317   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4318   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4319   PetscFunctionReturn(0);
4320 }
4321 
4322 /*@C
4323   DMPlexGetJoin - Get an array for the join of the set of points
4324 
4325   Not Collective
4326 
4327   Input Parameters:
4328 + dm - The `DMPLEX` object
4329 . numPoints - The number of input points for the join
4330 - points - The input points
4331 
4332   Output Parameters:
4333 + numCoveredPoints - The number of points in the join
4334 - coveredPoints - The points in the join
4335 
4336   Level: intermediate
4337 
4338   Note:
4339   Currently, this is restricted to a single level join
4340 
4341   Fortran Note:
4342   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4343 
4344 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4345 @*/
4346 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4347 {
4348   DM_Plex  *mesh = (DM_Plex *)dm->data;
4349   PetscInt *join[2];
4350   PetscInt  joinSize, i = 0;
4351   PetscInt  dof, off, p, c, m;
4352   PetscInt  maxSupportSize;
4353 
4354   PetscFunctionBegin;
4355   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4356   PetscValidIntPointer(points, 3);
4357   PetscValidIntPointer(numCoveredPoints, 4);
4358   PetscValidPointer(coveredPoints, 5);
4359   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4360   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4361   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4362   /* Copy in support of first point */
4363   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4364   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4365   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4366   /* Check each successive support */
4367   for (p = 1; p < numPoints; ++p) {
4368     PetscInt newJoinSize = 0;
4369 
4370     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4371     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4372     for (c = 0; c < dof; ++c) {
4373       const PetscInt point = mesh->supports[off + c];
4374 
4375       for (m = 0; m < joinSize; ++m) {
4376         if (point == join[i][m]) {
4377           join[1 - i][newJoinSize++] = point;
4378           break;
4379         }
4380       }
4381     }
4382     joinSize = newJoinSize;
4383     i        = 1 - i;
4384   }
4385   *numCoveredPoints = joinSize;
4386   *coveredPoints    = join[i];
4387   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4388   PetscFunctionReturn(0);
4389 }
4390 
4391 /*@C
4392   DMPlexRestoreJoin - Restore an array for the join of the set of points
4393 
4394   Not Collective
4395 
4396   Input Parameters:
4397 + dm - The `DMPLEX` object
4398 . numPoints - The number of input points for the join
4399 - points - The input points
4400 
4401   Output Parameters:
4402 + numCoveredPoints - The number of points in the join
4403 - coveredPoints - The points in the join
4404 
4405   Level: intermediate
4406 
4407   Fortran Note:
4408   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4409 
4410 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4411 @*/
4412 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4413 {
4414   PetscFunctionBegin;
4415   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4416   if (points) PetscValidIntPointer(points, 3);
4417   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4418   PetscValidPointer(coveredPoints, 5);
4419   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4420   if (numCoveredPoints) *numCoveredPoints = 0;
4421   PetscFunctionReturn(0);
4422 }
4423 
4424 /*@C
4425   DMPlexGetFullJoin - Get an array for the join of the set of points
4426 
4427   Not Collective
4428 
4429   Input Parameters:
4430 + dm - The `DMPLEX` object
4431 . numPoints - The number of input points for the join
4432 - points - The input points
4433 
4434   Output Parameters:
4435 + numCoveredPoints - The number of points in the join
4436 - coveredPoints - The points in the join
4437 
4438   Level: intermediate
4439 
4440   Fortran Note:
4441   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4442 
4443 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4444 @*/
4445 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4446 {
4447   PetscInt *offsets, **closures;
4448   PetscInt *join[2];
4449   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4450   PetscInt  p, d, c, m, ms;
4451 
4452   PetscFunctionBegin;
4453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4454   PetscValidIntPointer(points, 3);
4455   PetscValidIntPointer(numCoveredPoints, 4);
4456   PetscValidPointer(coveredPoints, 5);
4457 
4458   PetscCall(DMPlexGetDepth(dm, &depth));
4459   PetscCall(PetscCalloc1(numPoints, &closures));
4460   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4461   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4462   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4463   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4464   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4465 
4466   for (p = 0; p < numPoints; ++p) {
4467     PetscInt closureSize;
4468 
4469     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4470 
4471     offsets[p * (depth + 2) + 0] = 0;
4472     for (d = 0; d < depth + 1; ++d) {
4473       PetscInt pStart, pEnd, i;
4474 
4475       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4476       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4477         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4478           offsets[p * (depth + 2) + d + 1] = i;
4479           break;
4480         }
4481       }
4482       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4483     }
4484     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);
4485   }
4486   for (d = 0; d < depth + 1; ++d) {
4487     PetscInt dof;
4488 
4489     /* Copy in support of first point */
4490     dof = offsets[d + 1] - offsets[d];
4491     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4492     /* Check each successive cone */
4493     for (p = 1; p < numPoints && joinSize; ++p) {
4494       PetscInt newJoinSize = 0;
4495 
4496       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4497       for (c = 0; c < dof; ++c) {
4498         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4499 
4500         for (m = 0; m < joinSize; ++m) {
4501           if (point == join[i][m]) {
4502             join[1 - i][newJoinSize++] = point;
4503             break;
4504           }
4505         }
4506       }
4507       joinSize = newJoinSize;
4508       i        = 1 - i;
4509     }
4510     if (joinSize) break;
4511   }
4512   *numCoveredPoints = joinSize;
4513   *coveredPoints    = join[i];
4514   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4515   PetscCall(PetscFree(closures));
4516   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4517   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4518   PetscFunctionReturn(0);
4519 }
4520 
4521 /*@C
4522   DMPlexGetMeet - Get an array for the meet of the set of points
4523 
4524   Not Collective
4525 
4526   Input Parameters:
4527 + dm - The `DMPLEX` object
4528 . numPoints - The number of input points for the meet
4529 - points - The input points
4530 
4531   Output Parameters:
4532 + numCoveredPoints - The number of points in the meet
4533 - coveredPoints - The points in the meet
4534 
4535   Level: intermediate
4536 
4537   Note:
4538   Currently, this is restricted to a single level meet
4539 
4540   Fortran Notes:
4541   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4542 
4543 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4544 @*/
4545 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4546 {
4547   DM_Plex  *mesh = (DM_Plex *)dm->data;
4548   PetscInt *meet[2];
4549   PetscInt  meetSize, i = 0;
4550   PetscInt  dof, off, p, c, m;
4551   PetscInt  maxConeSize;
4552 
4553   PetscFunctionBegin;
4554   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4555   PetscValidIntPointer(points, 3);
4556   PetscValidIntPointer(numCoveringPoints, 4);
4557   PetscValidPointer(coveringPoints, 5);
4558   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4559   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4560   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4561   /* Copy in cone of first point */
4562   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4563   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4564   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4565   /* Check each successive cone */
4566   for (p = 1; p < numPoints; ++p) {
4567     PetscInt newMeetSize = 0;
4568 
4569     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4570     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4571     for (c = 0; c < dof; ++c) {
4572       const PetscInt point = mesh->cones[off + c];
4573 
4574       for (m = 0; m < meetSize; ++m) {
4575         if (point == meet[i][m]) {
4576           meet[1 - i][newMeetSize++] = point;
4577           break;
4578         }
4579       }
4580     }
4581     meetSize = newMeetSize;
4582     i        = 1 - i;
4583   }
4584   *numCoveringPoints = meetSize;
4585   *coveringPoints    = meet[i];
4586   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4587   PetscFunctionReturn(0);
4588 }
4589 
4590 /*@C
4591   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4592 
4593   Not Collective
4594 
4595   Input Parameters:
4596 + dm - The `DMPLEX` object
4597 . numPoints - The number of input points for the meet
4598 - points - The input points
4599 
4600   Output Parameters:
4601 + numCoveredPoints - The number of points in the meet
4602 - coveredPoints - The points in the meet
4603 
4604   Level: intermediate
4605 
4606   Fortran Note:
4607   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4608 
4609 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4610 @*/
4611 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4612 {
4613   PetscFunctionBegin;
4614   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4615   if (points) PetscValidIntPointer(points, 3);
4616   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4617   PetscValidPointer(coveredPoints, 5);
4618   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4619   if (numCoveredPoints) *numCoveredPoints = 0;
4620   PetscFunctionReturn(0);
4621 }
4622 
4623 /*@C
4624   DMPlexGetFullMeet - Get an array for the meet of the set of points
4625 
4626   Not Collective
4627 
4628   Input Parameters:
4629 + dm - The `DMPLEX` object
4630 . numPoints - The number of input points for the meet
4631 - points - The input points
4632 
4633   Output Parameters:
4634 + numCoveredPoints - The number of points in the meet
4635 - coveredPoints - The points in the meet
4636 
4637   Level: intermediate
4638 
4639   Fortran Note:
4640   The numCoveredPoints argument is not present in the Fortran binding since it is internal to the array.
4641 
4642 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4643 @*/
4644 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4645 {
4646   PetscInt *offsets, **closures;
4647   PetscInt *meet[2];
4648   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4649   PetscInt  p, h, c, m, mc;
4650 
4651   PetscFunctionBegin;
4652   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4653   PetscValidIntPointer(points, 3);
4654   PetscValidIntPointer(numCoveredPoints, 4);
4655   PetscValidPointer(coveredPoints, 5);
4656 
4657   PetscCall(DMPlexGetDepth(dm, &height));
4658   PetscCall(PetscMalloc1(numPoints, &closures));
4659   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4660   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4661   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4662   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4663   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4664 
4665   for (p = 0; p < numPoints; ++p) {
4666     PetscInt closureSize;
4667 
4668     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4669 
4670     offsets[p * (height + 2) + 0] = 0;
4671     for (h = 0; h < height + 1; ++h) {
4672       PetscInt pStart, pEnd, i;
4673 
4674       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4675       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4676         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4677           offsets[p * (height + 2) + h + 1] = i;
4678           break;
4679         }
4680       }
4681       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4682     }
4683     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);
4684   }
4685   for (h = 0; h < height + 1; ++h) {
4686     PetscInt dof;
4687 
4688     /* Copy in cone of first point */
4689     dof = offsets[h + 1] - offsets[h];
4690     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4691     /* Check each successive cone */
4692     for (p = 1; p < numPoints && meetSize; ++p) {
4693       PetscInt newMeetSize = 0;
4694 
4695       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4696       for (c = 0; c < dof; ++c) {
4697         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4698 
4699         for (m = 0; m < meetSize; ++m) {
4700           if (point == meet[i][m]) {
4701             meet[1 - i][newMeetSize++] = point;
4702             break;
4703           }
4704         }
4705       }
4706       meetSize = newMeetSize;
4707       i        = 1 - i;
4708     }
4709     if (meetSize) break;
4710   }
4711   *numCoveredPoints = meetSize;
4712   *coveredPoints    = meet[i];
4713   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4714   PetscCall(PetscFree(closures));
4715   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4716   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4717   PetscFunctionReturn(0);
4718 }
4719 
4720 /*@C
4721   DMPlexEqual - Determine if two `DM` have the same topology
4722 
4723   Not Collective
4724 
4725   Input Parameters:
4726 + dmA - A `DMPLEX` object
4727 - dmB - A `DMPLEX` object
4728 
4729   Output Parameters:
4730 . equal - `PETSC_TRUE` if the topologies are identical
4731 
4732   Level: intermediate
4733 
4734   Note:
4735   We are not solving graph isomorphism, so we do not permute.
4736 
4737 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4738 @*/
4739 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4740 {
4741   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4742 
4743   PetscFunctionBegin;
4744   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4745   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4746   PetscValidBoolPointer(equal, 3);
4747 
4748   *equal = PETSC_FALSE;
4749   PetscCall(DMPlexGetDepth(dmA, &depth));
4750   PetscCall(DMPlexGetDepth(dmB, &depthB));
4751   if (depth != depthB) PetscFunctionReturn(0);
4752   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4753   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4754   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4755   for (p = pStart; p < pEnd; ++p) {
4756     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4757     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4758 
4759     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4760     PetscCall(DMPlexGetCone(dmA, p, &cone));
4761     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4762     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4763     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4764     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4765     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4766     for (c = 0; c < coneSize; ++c) {
4767       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4768       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4769     }
4770     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4771     PetscCall(DMPlexGetSupport(dmA, p, &support));
4772     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4773     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4774     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4775     for (s = 0; s < supportSize; ++s) {
4776       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4777     }
4778   }
4779   *equal = PETSC_TRUE;
4780   PetscFunctionReturn(0);
4781 }
4782 
4783 /*@C
4784   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4785 
4786   Not Collective
4787 
4788   Input Parameters:
4789 + dm         - The `DMPLEX`
4790 . cellDim    - The cell dimension
4791 - numCorners - The number of vertices on a cell
4792 
4793   Output Parameters:
4794 . numFaceVertices - The number of vertices on a face
4795 
4796   Level: developer
4797 
4798   Note:
4799   Of course this can only work for a restricted set of symmetric shapes
4800 
4801 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4802 @*/
4803 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4804 {
4805   MPI_Comm comm;
4806 
4807   PetscFunctionBegin;
4808   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4809   PetscValidIntPointer(numFaceVertices, 4);
4810   switch (cellDim) {
4811   case 0:
4812     *numFaceVertices = 0;
4813     break;
4814   case 1:
4815     *numFaceVertices = 1;
4816     break;
4817   case 2:
4818     switch (numCorners) {
4819     case 3:                 /* triangle */
4820       *numFaceVertices = 2; /* Edge has 2 vertices */
4821       break;
4822     case 4:                 /* quadrilateral */
4823       *numFaceVertices = 2; /* Edge has 2 vertices */
4824       break;
4825     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4826       *numFaceVertices = 3; /* Edge has 3 vertices */
4827       break;
4828     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4829       *numFaceVertices = 3; /* Edge has 3 vertices */
4830       break;
4831     default:
4832       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4833     }
4834     break;
4835   case 3:
4836     switch (numCorners) {
4837     case 4:                 /* tetradehdron */
4838       *numFaceVertices = 3; /* Face has 3 vertices */
4839       break;
4840     case 6:                 /* tet cohesive cells */
4841       *numFaceVertices = 4; /* Face has 4 vertices */
4842       break;
4843     case 8:                 /* hexahedron */
4844       *numFaceVertices = 4; /* Face has 4 vertices */
4845       break;
4846     case 9:                 /* tet cohesive Lagrange cells */
4847       *numFaceVertices = 6; /* Face has 6 vertices */
4848       break;
4849     case 10:                /* quadratic tetrahedron */
4850       *numFaceVertices = 6; /* Face has 6 vertices */
4851       break;
4852     case 12:                /* hex cohesive Lagrange cells */
4853       *numFaceVertices = 6; /* Face has 6 vertices */
4854       break;
4855     case 18:                /* quadratic tet cohesive Lagrange cells */
4856       *numFaceVertices = 6; /* Face has 6 vertices */
4857       break;
4858     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4859       *numFaceVertices = 9; /* Face has 9 vertices */
4860       break;
4861     default:
4862       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4863     }
4864     break;
4865   default:
4866     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4867   }
4868   PetscFunctionReturn(0);
4869 }
4870 
4871 /*@
4872   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4873 
4874   Not Collective
4875 
4876   Input Parameter:
4877 . dm    - The `DMPLEX` object
4878 
4879   Output Parameter:
4880 . depthLabel - The `DMLabel` recording point depth
4881 
4882   Level: developer
4883 
4884 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4885 @*/
4886 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4887 {
4888   PetscFunctionBegin;
4889   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4890   PetscValidPointer(depthLabel, 2);
4891   *depthLabel = dm->depthLabel;
4892   PetscFunctionReturn(0);
4893 }
4894 
4895 /*@
4896   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4897 
4898   Not Collective
4899 
4900   Input Parameter:
4901 . dm    - The `DMPLEX` object
4902 
4903   Output Parameter:
4904 . depth - The number of strata (breadth first levels) in the DAG
4905 
4906   Level: developer
4907 
4908   Notes:
4909   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4910 
4911   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4912 
4913   An empty mesh gives -1.
4914 
4915 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4916 @*/
4917 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4918 {
4919   DM_Plex *mesh = (DM_Plex *)dm->data;
4920   DMLabel  label;
4921   PetscInt d = 0;
4922 
4923   PetscFunctionBegin;
4924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4925   PetscValidIntPointer(depth, 2);
4926   if (mesh->tr) {
4927     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4928   } else {
4929     PetscCall(DMPlexGetDepthLabel(dm, &label));
4930     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4931     *depth = d - 1;
4932   }
4933   PetscFunctionReturn(0);
4934 }
4935 
4936 /*@
4937   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4938 
4939   Not Collective
4940 
4941   Input Parameters:
4942 + dm    - The `DMPLEX` object
4943 - depth - The requested depth
4944 
4945   Output Parameters:
4946 + start - The first point at this depth
4947 - end   - One beyond the last point at this depth
4948 
4949   Level: developer
4950 
4951   Notes:
4952   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4953   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
4954   higher dimension, e.g., "edges".
4955 
4956 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4957 @*/
4958 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
4959 {
4960   DM_Plex *mesh = (DM_Plex *)dm->data;
4961   DMLabel  label;
4962   PetscInt pStart, pEnd;
4963 
4964   PetscFunctionBegin;
4965   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4966   if (start) {
4967     PetscValidIntPointer(start, 3);
4968     *start = 0;
4969   }
4970   if (end) {
4971     PetscValidIntPointer(end, 4);
4972     *end = 0;
4973   }
4974   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4975   if (pStart == pEnd) PetscFunctionReturn(0);
4976   if (depth < 0) {
4977     if (start) *start = pStart;
4978     if (end) *end = pEnd;
4979     PetscFunctionReturn(0);
4980   }
4981   if (mesh->tr) {
4982     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
4983   } else {
4984     PetscCall(DMPlexGetDepthLabel(dm, &label));
4985     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4986     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4987   }
4988   PetscFunctionReturn(0);
4989 }
4990 
4991 /*@
4992   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4993 
4994   Not Collective
4995 
4996   Input Parameters:
4997 + dm     - The `DMPLEX` object
4998 - height - The requested height
4999 
5000   Output Parameters:
5001 + start - The first point at this height
5002 - end   - One beyond the last point at this height
5003 
5004   Level: developer
5005 
5006   Notes:
5007   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5008   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5009   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5010 
5011 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5012 @*/
5013 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5014 {
5015   DMLabel  label;
5016   PetscInt depth, pStart, pEnd;
5017 
5018   PetscFunctionBegin;
5019   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5020   if (start) {
5021     PetscValidIntPointer(start, 3);
5022     *start = 0;
5023   }
5024   if (end) {
5025     PetscValidIntPointer(end, 4);
5026     *end = 0;
5027   }
5028   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5029   if (pStart == pEnd) PetscFunctionReturn(0);
5030   if (height < 0) {
5031     if (start) *start = pStart;
5032     if (end) *end = pEnd;
5033     PetscFunctionReturn(0);
5034   }
5035   PetscCall(DMPlexGetDepthLabel(dm, &label));
5036   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5037   PetscCall(DMLabelGetNumValues(label, &depth));
5038   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
5039   PetscFunctionReturn(0);
5040 }
5041 
5042 /*@
5043   DMPlexGetPointDepth - Get the depth of a given point
5044 
5045   Not Collective
5046 
5047   Input Parameters:
5048 + dm    - The `DMPLEX` object
5049 - point - The point
5050 
5051   Output Parameter:
5052 . depth - The depth of the point
5053 
5054   Level: intermediate
5055 
5056 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5057 @*/
5058 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5059 {
5060   PetscFunctionBegin;
5061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5062   PetscValidIntPointer(depth, 3);
5063   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5064   PetscFunctionReturn(0);
5065 }
5066 
5067 /*@
5068   DMPlexGetPointHeight - Get the height of a given point
5069 
5070   Not Collective
5071 
5072   Input Parameters:
5073 + dm    - The `DMPLEX` object
5074 - point - The point
5075 
5076   Output Parameter:
5077 . height - The height of the point
5078 
5079   Level: intermediate
5080 
5081 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5082 @*/
5083 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5084 {
5085   PetscInt n, pDepth;
5086 
5087   PetscFunctionBegin;
5088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5089   PetscValidIntPointer(height, 3);
5090   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5091   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5092   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5093   PetscFunctionReturn(0);
5094 }
5095 
5096 /*@
5097   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5098 
5099   Not Collective
5100 
5101   Input Parameter:
5102 . dm - The `DMPLEX` object
5103 
5104   Output Parameter:
5105 . celltypeLabel - The `DMLabel` recording cell polytope type
5106 
5107   Level: developer
5108 
5109   Note:
5110   This function will trigger automatica computation of cell types. This can be disabled by calling
5111   `DMCreateLabel`(dm, "celltype") beforehand.
5112 
5113 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5114 @*/
5115 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5116 {
5117   PetscFunctionBegin;
5118   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5119   PetscValidPointer(celltypeLabel, 2);
5120   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5121   *celltypeLabel = dm->celltypeLabel;
5122   PetscFunctionReturn(0);
5123 }
5124 
5125 /*@
5126   DMPlexGetCellType - Get the polytope type of a given cell
5127 
5128   Not Collective
5129 
5130   Input Parameters:
5131 + dm   - The `DMPLEX` object
5132 - cell - The cell
5133 
5134   Output Parameter:
5135 . celltype - The polytope type of the cell
5136 
5137   Level: intermediate
5138 
5139 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5140 @*/
5141 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5142 {
5143   DM_Plex *mesh = (DM_Plex *)dm->data;
5144   DMLabel  label;
5145   PetscInt ct;
5146 
5147   PetscFunctionBegin;
5148   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5149   PetscValidPointer(celltype, 3);
5150   if (mesh->tr) {
5151     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5152   } else {
5153     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5154     PetscCall(DMLabelGetValue(label, cell, &ct));
5155     PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5156     *celltype = (DMPolytopeType)ct;
5157   }
5158   PetscFunctionReturn(0);
5159 }
5160 
5161 /*@
5162   DMPlexSetCellType - Set the polytope type of a given cell
5163 
5164   Not Collective
5165 
5166   Input Parameters:
5167 + dm   - The `DMPLEX` object
5168 . cell - The cell
5169 - celltype - The polytope type of the cell
5170 
5171   Level: advanced
5172 
5173   Note:
5174   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5175   is executed. This function will override the computed type. However, if automatic classification will not succeed
5176   and a user wants to manually specify all types, the classification must be disabled by calling
5177   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
5178 
5179 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5180 @*/
5181 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5182 {
5183   DMLabel label;
5184 
5185   PetscFunctionBegin;
5186   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5187   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5188   PetscCall(DMLabelSetValue(label, cell, celltype));
5189   PetscFunctionReturn(0);
5190 }
5191 
5192 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5193 {
5194   PetscSection section, s;
5195   Mat          m;
5196   PetscInt     maxHeight;
5197   const char  *prefix;
5198 
5199   PetscFunctionBegin;
5200   PetscCall(DMClone(dm, cdm));
5201   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5202   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5203   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5204   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5205   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5206   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5207   PetscCall(DMSetLocalSection(*cdm, section));
5208   PetscCall(PetscSectionDestroy(&section));
5209   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5210   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5211   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5212   PetscCall(PetscSectionDestroy(&s));
5213   PetscCall(MatDestroy(&m));
5214 
5215   PetscCall(DMSetNumFields(*cdm, 1));
5216   PetscCall(DMCreateDS(*cdm));
5217   (*cdm)->cloneOpts = PETSC_TRUE;
5218   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5219   PetscFunctionReturn(0);
5220 }
5221 
5222 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5223 {
5224   Vec coordsLocal, cellCoordsLocal;
5225   DM  coordsDM, cellCoordsDM;
5226 
5227   PetscFunctionBegin;
5228   *field = NULL;
5229   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5230   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5231   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5232   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5233   if (coordsLocal && coordsDM) {
5234     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5235     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5236   }
5237   PetscFunctionReturn(0);
5238 }
5239 
5240 /*@C
5241   DMPlexGetConeSection - Return a section which describes the layout of cone data
5242 
5243   Not Collective
5244 
5245   Input Parameters:
5246 . dm        - The `DMPLEX` object
5247 
5248   Output Parameter:
5249 . section - The `PetscSection` object
5250 
5251   Level: developer
5252 
5253 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5254 @*/
5255 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5256 {
5257   DM_Plex *mesh = (DM_Plex *)dm->data;
5258 
5259   PetscFunctionBegin;
5260   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5261   if (section) *section = mesh->coneSection;
5262   PetscFunctionReturn(0);
5263 }
5264 
5265 /*@C
5266   DMPlexGetSupportSection - Return a section which describes the layout of support data
5267 
5268   Not Collective
5269 
5270   Input Parameters:
5271 . dm        - The `DMPLEX` object
5272 
5273   Output Parameter:
5274 . section - The `PetscSection` object
5275 
5276   Level: developer
5277 
5278 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5279 @*/
5280 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5281 {
5282   DM_Plex *mesh = (DM_Plex *)dm->data;
5283 
5284   PetscFunctionBegin;
5285   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5286   if (section) *section = mesh->supportSection;
5287   PetscFunctionReturn(0);
5288 }
5289 
5290 /*@C
5291   DMPlexGetCones - Return cone data
5292 
5293   Not Collective
5294 
5295   Input Parameters:
5296 . dm        - The `DMPLEX` object
5297 
5298   Output Parameter:
5299 . cones - The cone for each point
5300 
5301   Level: developer
5302 
5303 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5304 @*/
5305 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5306 {
5307   DM_Plex *mesh = (DM_Plex *)dm->data;
5308 
5309   PetscFunctionBegin;
5310   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5311   if (cones) *cones = mesh->cones;
5312   PetscFunctionReturn(0);
5313 }
5314 
5315 /*@C
5316   DMPlexGetConeOrientations - Return cone orientation data
5317 
5318   Not Collective
5319 
5320   Input Parameters:
5321 . dm        - The `DMPLEX` object
5322 
5323   Output Parameter:
5324 . coneOrientations - The array of cone orientations for all points
5325 
5326   Level: developer
5327 
5328   Notes:
5329   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5330 
5331   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5332 
5333 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5334 @*/
5335 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5336 {
5337   DM_Plex *mesh = (DM_Plex *)dm->data;
5338 
5339   PetscFunctionBegin;
5340   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5341   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5342   PetscFunctionReturn(0);
5343 }
5344 
5345 /******************************** FEM Support **********************************/
5346 
5347 /*
5348  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5349  representing a line in the section.
5350 */
5351 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5352 {
5353   PetscFunctionBeginHot;
5354   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5355   if (line < 0) {
5356     *k  = 0;
5357     *Nc = 0;
5358   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5359     *k = 1;
5360   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5361     /* An order k SEM disc has k-1 dofs on an edge */
5362     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5363     *k = *k / *Nc + 1;
5364   }
5365   PetscFunctionReturn(0);
5366 }
5367 
5368 /*@
5369 
5370   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5371   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5372   section provided (or the section of the DM).
5373 
5374   Input Parameters:
5375 + dm      - The DM
5376 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5377 - section - The PetscSection to reorder, or NULL for the default section
5378 
5379   Example:
5380   A typical interpolated single-quad mesh might order points as
5381 .vb
5382   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5383 
5384   v4 -- e6 -- v3
5385   |           |
5386   e7    c0    e8
5387   |           |
5388   v1 -- e5 -- v2
5389 .ve
5390 
5391   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5392   dofs in the order of points, e.g.,
5393 .vb
5394     c0 -> [0,1,2,3]
5395     v1 -> [4]
5396     ...
5397     e5 -> [8, 9]
5398 .ve
5399 
5400   which corresponds to the dofs
5401 .vb
5402     6   10  11  7
5403     13  2   3   15
5404     12  0   1   14
5405     4   8   9   5
5406 .ve
5407 
5408   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5409 .vb
5410   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5411 .ve
5412 
5413   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5414 .vb
5415    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5416 .ve
5417 
5418   Level: developer
5419 
5420   Note:
5421   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5422   degree of the basis.
5423 
5424 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5425 @*/
5426 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5427 {
5428   DMLabel   label;
5429   PetscInt  dim, depth = -1, eStart = -1, Nf;
5430   PetscBool vertexchart;
5431 
5432   PetscFunctionBegin;
5433   PetscCall(DMGetDimension(dm, &dim));
5434   if (dim < 1) PetscFunctionReturn(0);
5435   if (point < 0) {
5436     PetscInt sStart, sEnd;
5437 
5438     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5439     point = sEnd - sStart ? sStart : point;
5440   }
5441   PetscCall(DMPlexGetDepthLabel(dm, &label));
5442   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5443   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5444   if (depth == 1) {
5445     eStart = point;
5446   } else if (depth == dim) {
5447     const PetscInt *cone;
5448 
5449     PetscCall(DMPlexGetCone(dm, point, &cone));
5450     if (dim == 2) eStart = cone[0];
5451     else if (dim == 3) {
5452       const PetscInt *cone2;
5453       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5454       eStart = cone2[0];
5455     } 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);
5456   } 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);
5457   { /* Determine whether the chart covers all points or just vertices. */
5458     PetscInt pStart, pEnd, cStart, cEnd;
5459     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5460     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5461     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5462     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5463     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5464   }
5465   PetscCall(PetscSectionGetNumFields(section, &Nf));
5466   for (PetscInt d = 1; d <= dim; d++) {
5467     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5468     PetscInt *perm;
5469 
5470     for (f = 0; f < Nf; ++f) {
5471       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5472       size += PetscPowInt(k + 1, d) * Nc;
5473     }
5474     PetscCall(PetscMalloc1(size, &perm));
5475     for (f = 0; f < Nf; ++f) {
5476       switch (d) {
5477       case 1:
5478         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5479         /*
5480          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5481          We want              [ vtx0; edge of length k-1; vtx1 ]
5482          */
5483         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5484         for (i = 0; i < k - 1; i++)
5485           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5486         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5487         foffset = offset;
5488         break;
5489       case 2:
5490         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5491         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5492         /* The SEM order is
5493 
5494          v_lb, {e_b}, v_rb,
5495          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5496          v_lt, reverse {e_t}, v_rt
5497          */
5498         {
5499           const PetscInt of   = 0;
5500           const PetscInt oeb  = of + PetscSqr(k - 1);
5501           const PetscInt oer  = oeb + (k - 1);
5502           const PetscInt oet  = oer + (k - 1);
5503           const PetscInt oel  = oet + (k - 1);
5504           const PetscInt ovlb = oel + (k - 1);
5505           const PetscInt ovrb = ovlb + 1;
5506           const PetscInt ovrt = ovrb + 1;
5507           const PetscInt ovlt = ovrt + 1;
5508           PetscInt       o;
5509 
5510           /* bottom */
5511           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5512           for (o = oeb; o < oer; ++o)
5513             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5514           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5515           /* middle */
5516           for (i = 0; i < k - 1; ++i) {
5517             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5518             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5519               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5520             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5521           }
5522           /* top */
5523           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5524           for (o = oel - 1; o >= oet; --o)
5525             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5526           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5527           foffset = offset;
5528         }
5529         break;
5530       case 3:
5531         /* The original hex closure is
5532 
5533          {c,
5534          f_b, f_t, f_f, f_b, f_r, f_l,
5535          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5536          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5537          */
5538         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5539         /* The SEM order is
5540          Bottom Slice
5541          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5542          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5543          v_blb, {e_bb}, v_brb,
5544 
5545          Middle Slice (j)
5546          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5547          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5548          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5549 
5550          Top Slice
5551          v_tlf, {e_tf}, v_trf,
5552          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5553          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5554          */
5555         {
5556           const PetscInt oc    = 0;
5557           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5558           const PetscInt oft   = ofb + PetscSqr(k - 1);
5559           const PetscInt off   = oft + PetscSqr(k - 1);
5560           const PetscInt ofk   = off + PetscSqr(k - 1);
5561           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5562           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5563           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5564           const PetscInt oebb  = oebl + (k - 1);
5565           const PetscInt oebr  = oebb + (k - 1);
5566           const PetscInt oebf  = oebr + (k - 1);
5567           const PetscInt oetf  = oebf + (k - 1);
5568           const PetscInt oetr  = oetf + (k - 1);
5569           const PetscInt oetb  = oetr + (k - 1);
5570           const PetscInt oetl  = oetb + (k - 1);
5571           const PetscInt oerf  = oetl + (k - 1);
5572           const PetscInt oelf  = oerf + (k - 1);
5573           const PetscInt oelb  = oelf + (k - 1);
5574           const PetscInt oerb  = oelb + (k - 1);
5575           const PetscInt ovblf = oerb + (k - 1);
5576           const PetscInt ovblb = ovblf + 1;
5577           const PetscInt ovbrb = ovblb + 1;
5578           const PetscInt ovbrf = ovbrb + 1;
5579           const PetscInt ovtlf = ovbrf + 1;
5580           const PetscInt ovtrf = ovtlf + 1;
5581           const PetscInt ovtrb = ovtrf + 1;
5582           const PetscInt ovtlb = ovtrb + 1;
5583           PetscInt       o, n;
5584 
5585           /* Bottom Slice */
5586           /*   bottom */
5587           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5588           for (o = oetf - 1; o >= oebf; --o)
5589             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5590           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5591           /*   middle */
5592           for (i = 0; i < k - 1; ++i) {
5593             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5594             for (n = 0; n < k - 1; ++n) {
5595               o = ofb + n * (k - 1) + i;
5596               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5597             }
5598             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5599           }
5600           /*   top */
5601           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5602           for (o = oebb; o < oebr; ++o)
5603             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5604           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5605 
5606           /* Middle Slice */
5607           for (j = 0; j < k - 1; ++j) {
5608             /*   bottom */
5609             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5610             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5611               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5612             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5613             /*   middle */
5614             for (i = 0; i < k - 1; ++i) {
5615               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5616               for (n = 0; n < k - 1; ++n)
5617                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5618               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5619             }
5620             /*   top */
5621             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5622             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5623               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5624             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5625           }
5626 
5627           /* Top Slice */
5628           /*   bottom */
5629           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5630           for (o = oetf; o < oetr; ++o)
5631             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5632           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5633           /*   middle */
5634           for (i = 0; i < k - 1; ++i) {
5635             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5636             for (n = 0; n < k - 1; ++n)
5637               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5638             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5639           }
5640           /*   top */
5641           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5642           for (o = oetl - 1; o >= oetb; --o)
5643             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5644           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5645 
5646           foffset = offset;
5647         }
5648         break;
5649       default:
5650         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5651       }
5652     }
5653     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5654     /* Check permutation */
5655     {
5656       PetscInt *check;
5657 
5658       PetscCall(PetscMalloc1(size, &check));
5659       for (i = 0; i < size; ++i) {
5660         check[i] = -1;
5661         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5662       }
5663       for (i = 0; i < size; ++i) check[perm[i]] = i;
5664       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5665       PetscCall(PetscFree(check));
5666     }
5667     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5668     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5669       PetscInt *loc_perm;
5670       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5671       for (PetscInt i = 0; i < size; i++) {
5672         loc_perm[i]        = perm[i];
5673         loc_perm[size + i] = size + perm[i];
5674       }
5675       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5676     }
5677   }
5678   PetscFunctionReturn(0);
5679 }
5680 
5681 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5682 {
5683   PetscDS  prob;
5684   PetscInt depth, Nf, h;
5685   DMLabel  label;
5686 
5687   PetscFunctionBeginHot;
5688   PetscCall(DMGetDS(dm, &prob));
5689   Nf      = prob->Nf;
5690   label   = dm->depthLabel;
5691   *dspace = NULL;
5692   if (field < Nf) {
5693     PetscObject disc = prob->disc[field];
5694 
5695     if (disc->classid == PETSCFE_CLASSID) {
5696       PetscDualSpace dsp;
5697 
5698       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5699       PetscCall(DMLabelGetNumValues(label, &depth));
5700       PetscCall(DMLabelGetValue(label, point, &h));
5701       h = depth - 1 - h;
5702       if (h) {
5703         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5704       } else {
5705         *dspace = dsp;
5706       }
5707     }
5708   }
5709   PetscFunctionReturn(0);
5710 }
5711 
5712 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5713 {
5714   PetscScalar       *array;
5715   const PetscScalar *vArray;
5716   const PetscInt    *cone, *coneO;
5717   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5718 
5719   PetscFunctionBeginHot;
5720   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5721   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5722   PetscCall(DMPlexGetCone(dm, point, &cone));
5723   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5724   if (!values || !*values) {
5725     if ((point >= pStart) && (point < pEnd)) {
5726       PetscInt dof;
5727 
5728       PetscCall(PetscSectionGetDof(section, point, &dof));
5729       size += dof;
5730     }
5731     for (p = 0; p < numPoints; ++p) {
5732       const PetscInt cp = cone[p];
5733       PetscInt       dof;
5734 
5735       if ((cp < pStart) || (cp >= pEnd)) continue;
5736       PetscCall(PetscSectionGetDof(section, cp, &dof));
5737       size += dof;
5738     }
5739     if (!values) {
5740       if (csize) *csize = size;
5741       PetscFunctionReturn(0);
5742     }
5743     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5744   } else {
5745     array = *values;
5746   }
5747   size = 0;
5748   PetscCall(VecGetArrayRead(v, &vArray));
5749   if ((point >= pStart) && (point < pEnd)) {
5750     PetscInt           dof, off, d;
5751     const PetscScalar *varr;
5752 
5753     PetscCall(PetscSectionGetDof(section, point, &dof));
5754     PetscCall(PetscSectionGetOffset(section, point, &off));
5755     varr = &vArray[off];
5756     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5757     size += dof;
5758   }
5759   for (p = 0; p < numPoints; ++p) {
5760     const PetscInt     cp = cone[p];
5761     PetscInt           o  = coneO[p];
5762     PetscInt           dof, off, d;
5763     const PetscScalar *varr;
5764 
5765     if ((cp < pStart) || (cp >= pEnd)) continue;
5766     PetscCall(PetscSectionGetDof(section, cp, &dof));
5767     PetscCall(PetscSectionGetOffset(section, cp, &off));
5768     varr = &vArray[off];
5769     if (o >= 0) {
5770       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5771     } else {
5772       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5773     }
5774     size += dof;
5775   }
5776   PetscCall(VecRestoreArrayRead(v, &vArray));
5777   if (!*values) {
5778     if (csize) *csize = size;
5779     *values = array;
5780   } else {
5781     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5782     *csize = size;
5783   }
5784   PetscFunctionReturn(0);
5785 }
5786 
5787 /* Compress out points not in the section */
5788 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5789 {
5790   const PetscInt np = *numPoints;
5791   PetscInt       pStart, pEnd, p, q;
5792 
5793   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5794   for (p = 0, q = 0; p < np; ++p) {
5795     const PetscInt r = points[p * 2];
5796     if ((r >= pStart) && (r < pEnd)) {
5797       points[q * 2]     = r;
5798       points[q * 2 + 1] = points[p * 2 + 1];
5799       ++q;
5800     }
5801   }
5802   *numPoints = q;
5803   return 0;
5804 }
5805 
5806 /* Compressed closure does not apply closure permutation */
5807 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5808 {
5809   const PetscInt *cla = NULL;
5810   PetscInt        np, *pts = NULL;
5811 
5812   PetscFunctionBeginHot;
5813   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5814   if (*clPoints) {
5815     PetscInt dof, off;
5816 
5817     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5818     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5819     PetscCall(ISGetIndices(*clPoints, &cla));
5820     np  = dof / 2;
5821     pts = (PetscInt *)&cla[off];
5822   } else {
5823     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5824     PetscCall(CompressPoints_Private(section, &np, pts));
5825   }
5826   *numPoints = np;
5827   *points    = pts;
5828   *clp       = cla;
5829   PetscFunctionReturn(0);
5830 }
5831 
5832 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5833 {
5834   PetscFunctionBeginHot;
5835   if (!*clPoints) {
5836     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5837   } else {
5838     PetscCall(ISRestoreIndices(*clPoints, clp));
5839   }
5840   *numPoints = 0;
5841   *points    = NULL;
5842   *clSec     = NULL;
5843   *clPoints  = NULL;
5844   *clp       = NULL;
5845   PetscFunctionReturn(0);
5846 }
5847 
5848 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5849 {
5850   PetscInt            offset = 0, p;
5851   const PetscInt    **perms  = NULL;
5852   const PetscScalar **flips  = NULL;
5853 
5854   PetscFunctionBeginHot;
5855   *size = 0;
5856   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5857   for (p = 0; p < numPoints; p++) {
5858     const PetscInt     point = points[2 * p];
5859     const PetscInt    *perm  = perms ? perms[p] : NULL;
5860     const PetscScalar *flip  = flips ? flips[p] : NULL;
5861     PetscInt           dof, off, d;
5862     const PetscScalar *varr;
5863 
5864     PetscCall(PetscSectionGetDof(section, point, &dof));
5865     PetscCall(PetscSectionGetOffset(section, point, &off));
5866     varr = &vArray[off];
5867     if (clperm) {
5868       if (perm) {
5869         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5870       } else {
5871         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5872       }
5873       if (flip) {
5874         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5875       }
5876     } else {
5877       if (perm) {
5878         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5879       } else {
5880         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5881       }
5882       if (flip) {
5883         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5884       }
5885     }
5886     offset += dof;
5887   }
5888   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5889   *size = offset;
5890   PetscFunctionReturn(0);
5891 }
5892 
5893 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[])
5894 {
5895   PetscInt offset = 0, f;
5896 
5897   PetscFunctionBeginHot;
5898   *size = 0;
5899   for (f = 0; f < numFields; ++f) {
5900     PetscInt            p;
5901     const PetscInt    **perms = NULL;
5902     const PetscScalar **flips = NULL;
5903 
5904     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5905     for (p = 0; p < numPoints; p++) {
5906       const PetscInt     point = points[2 * p];
5907       PetscInt           fdof, foff, b;
5908       const PetscScalar *varr;
5909       const PetscInt    *perm = perms ? perms[p] : NULL;
5910       const PetscScalar *flip = flips ? flips[p] : NULL;
5911 
5912       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5913       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5914       varr = &vArray[foff];
5915       if (clperm) {
5916         if (perm) {
5917           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5918         } else {
5919           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5920         }
5921         if (flip) {
5922           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5923         }
5924       } else {
5925         if (perm) {
5926           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5927         } else {
5928           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5929         }
5930         if (flip) {
5931           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5932         }
5933       }
5934       offset += fdof;
5935     }
5936     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5937   }
5938   *size = offset;
5939   PetscFunctionReturn(0);
5940 }
5941 
5942 /*@C
5943   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5944 
5945   Not collective
5946 
5947   Input Parameters:
5948 + dm - The `DM`
5949 . section - The section describing the layout in v, or NULL to use the default section
5950 . v - The local vector
5951 - point - The point in the `DM`
5952 
5953   Input/Output Parameters:
5954 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5955 - values - An array to use for the values, or NULL to have it allocated automatically;
5956            if the user provided NULL, it is a borrowed array and should not be freed
5957 
5958 
5959   Level: intermediate
5960 
5961   Notes:
5962   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to NULL in the
5963   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
5964   assembly function, and a user may already have allocated storage for this operation.
5965 
5966   A typical use could be
5967 .vb
5968    values = NULL;
5969    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5970    for (cl = 0; cl < clSize; ++cl) {
5971      <Compute on closure>
5972    }
5973    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5974 .ve
5975   or
5976 .vb
5977    PetscMalloc1(clMaxSize, &values);
5978    for (p = pStart; p < pEnd; ++p) {
5979      clSize = clMaxSize;
5980      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5981      for (cl = 0; cl < clSize; ++cl) {
5982        <Compute on closure>
5983      }
5984    }
5985    PetscFree(values);
5986 .ve
5987 
5988   Fortran Note:
5989   The csize argument is not present in the Fortran binding since it is internal to the array.
5990 
5991 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5992 @*/
5993 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5994 {
5995   PetscSection    clSection;
5996   IS              clPoints;
5997   PetscInt       *points = NULL;
5998   const PetscInt *clp, *perm;
5999   PetscInt        depth, numFields, numPoints, asize;
6000 
6001   PetscFunctionBeginHot;
6002   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6003   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6004   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6005   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6006   PetscCall(DMPlexGetDepth(dm, &depth));
6007   PetscCall(PetscSectionGetNumFields(section, &numFields));
6008   if (depth == 1 && numFields < 2) {
6009     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6010     PetscFunctionReturn(0);
6011   }
6012   /* Get points */
6013   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6014   /* Get sizes */
6015   asize = 0;
6016   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6017     PetscInt dof;
6018     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6019     asize += dof;
6020   }
6021   if (values) {
6022     const PetscScalar *vArray;
6023     PetscInt           size;
6024 
6025     if (*values) {
6026       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);
6027     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6028     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6029     PetscCall(VecGetArrayRead(v, &vArray));
6030     /* Get values */
6031     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6032     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6033     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6034     /* Cleanup array */
6035     PetscCall(VecRestoreArrayRead(v, &vArray));
6036   }
6037   if (csize) *csize = asize;
6038   /* Cleanup points */
6039   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6040   PetscFunctionReturn(0);
6041 }
6042 
6043 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6044 {
6045   DMLabel            depthLabel;
6046   PetscSection       clSection;
6047   IS                 clPoints;
6048   PetscScalar       *array;
6049   const PetscScalar *vArray;
6050   PetscInt          *points = NULL;
6051   const PetscInt    *clp, *perm = NULL;
6052   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6053 
6054   PetscFunctionBeginHot;
6055   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6056   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6057   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6058   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6059   PetscCall(DMPlexGetDepth(dm, &mdepth));
6060   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6061   PetscCall(PetscSectionGetNumFields(section, &numFields));
6062   if (mdepth == 1 && numFields < 2) {
6063     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6064     PetscFunctionReturn(0);
6065   }
6066   /* Get points */
6067   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6068   for (clsize = 0, p = 0; p < Np; p++) {
6069     PetscInt dof;
6070     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6071     clsize += dof;
6072   }
6073   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6074   /* Filter points */
6075   for (p = 0; p < numPoints * 2; p += 2) {
6076     PetscInt dep;
6077 
6078     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6079     if (dep != depth) continue;
6080     points[Np * 2 + 0] = points[p];
6081     points[Np * 2 + 1] = points[p + 1];
6082     ++Np;
6083   }
6084   /* Get array */
6085   if (!values || !*values) {
6086     PetscInt asize = 0, dof;
6087 
6088     for (p = 0; p < Np * 2; p += 2) {
6089       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6090       asize += dof;
6091     }
6092     if (!values) {
6093       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6094       if (csize) *csize = asize;
6095       PetscFunctionReturn(0);
6096     }
6097     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6098   } else {
6099     array = *values;
6100   }
6101   PetscCall(VecGetArrayRead(v, &vArray));
6102   /* Get values */
6103   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6104   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6105   /* Cleanup points */
6106   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6107   /* Cleanup array */
6108   PetscCall(VecRestoreArrayRead(v, &vArray));
6109   if (!*values) {
6110     if (csize) *csize = size;
6111     *values = array;
6112   } else {
6113     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6114     *csize = size;
6115   }
6116   PetscFunctionReturn(0);
6117 }
6118 
6119 /*@C
6120   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6121 
6122   Not collective
6123 
6124   Input Parameters:
6125 + dm - The `DM`
6126 . section - The section describing the layout in v, or NULL to use the default section
6127 . v - The local vector
6128 . point - The point in the `DM`
6129 . csize - The number of values in the closure, or NULL
6130 - values - The array of values, which is a borrowed array and should not be freed
6131 
6132   Level: intermediate
6133 
6134   Note:
6135   The array values are discarded and not copied back into v. In order to copy values back to v, use `DMPlexVecSetClosure()`
6136 
6137   Fortran Note:
6138   The csize argument is not present in the Fortran binding since it is internal to the array.
6139 
6140 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6141 @*/
6142 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6143 {
6144   PetscInt size = 0;
6145 
6146   PetscFunctionBegin;
6147   /* Should work without recalculating size */
6148   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6149   *values = NULL;
6150   PetscFunctionReturn(0);
6151 }
6152 
6153 static inline void add(PetscScalar *x, PetscScalar y)
6154 {
6155   *x += y;
6156 }
6157 static inline void insert(PetscScalar *x, PetscScalar y)
6158 {
6159   *x = y;
6160 }
6161 
6162 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[])
6163 {
6164   PetscInt        cdof;  /* The number of constraints on this point */
6165   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6166   PetscScalar    *a;
6167   PetscInt        off, cind = 0, k;
6168 
6169   PetscFunctionBegin;
6170   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6171   PetscCall(PetscSectionGetOffset(section, point, &off));
6172   a = &array[off];
6173   if (!cdof || setBC) {
6174     if (clperm) {
6175       if (perm) {
6176         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6177       } else {
6178         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6179       }
6180     } else {
6181       if (perm) {
6182         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6183       } else {
6184         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6185       }
6186     }
6187   } else {
6188     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6189     if (clperm) {
6190       if (perm) {
6191         for (k = 0; k < dof; ++k) {
6192           if ((cind < cdof) && (k == cdofs[cind])) {
6193             ++cind;
6194             continue;
6195           }
6196           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6197         }
6198       } else {
6199         for (k = 0; k < dof; ++k) {
6200           if ((cind < cdof) && (k == cdofs[cind])) {
6201             ++cind;
6202             continue;
6203           }
6204           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6205         }
6206       }
6207     } else {
6208       if (perm) {
6209         for (k = 0; k < dof; ++k) {
6210           if ((cind < cdof) && (k == cdofs[cind])) {
6211             ++cind;
6212             continue;
6213           }
6214           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6215         }
6216       } else {
6217         for (k = 0; k < dof; ++k) {
6218           if ((cind < cdof) && (k == cdofs[cind])) {
6219             ++cind;
6220             continue;
6221           }
6222           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6223         }
6224       }
6225     }
6226   }
6227   PetscFunctionReturn(0);
6228 }
6229 
6230 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[])
6231 {
6232   PetscInt        cdof;  /* The number of constraints on this point */
6233   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6234   PetscScalar    *a;
6235   PetscInt        off, cind = 0, k;
6236 
6237   PetscFunctionBegin;
6238   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6239   PetscCall(PetscSectionGetOffset(section, point, &off));
6240   a = &array[off];
6241   if (cdof) {
6242     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6243     if (clperm) {
6244       if (perm) {
6245         for (k = 0; k < dof; ++k) {
6246           if ((cind < cdof) && (k == cdofs[cind])) {
6247             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6248             cind++;
6249           }
6250         }
6251       } else {
6252         for (k = 0; k < dof; ++k) {
6253           if ((cind < cdof) && (k == cdofs[cind])) {
6254             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6255             cind++;
6256           }
6257         }
6258       }
6259     } else {
6260       if (perm) {
6261         for (k = 0; k < dof; ++k) {
6262           if ((cind < cdof) && (k == cdofs[cind])) {
6263             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6264             cind++;
6265           }
6266         }
6267       } else {
6268         for (k = 0; k < dof; ++k) {
6269           if ((cind < cdof) && (k == cdofs[cind])) {
6270             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6271             cind++;
6272           }
6273         }
6274       }
6275     }
6276   }
6277   PetscFunctionReturn(0);
6278 }
6279 
6280 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[])
6281 {
6282   PetscScalar    *a;
6283   PetscInt        fdof, foff, fcdof, foffset = *offset;
6284   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6285   PetscInt        cind = 0, b;
6286 
6287   PetscFunctionBegin;
6288   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6289   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6290   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6291   a = &array[foff];
6292   if (!fcdof || setBC) {
6293     if (clperm) {
6294       if (perm) {
6295         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6296       } else {
6297         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6298       }
6299     } else {
6300       if (perm) {
6301         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6302       } else {
6303         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6304       }
6305     }
6306   } else {
6307     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6308     if (clperm) {
6309       if (perm) {
6310         for (b = 0; b < fdof; b++) {
6311           if ((cind < fcdof) && (b == fcdofs[cind])) {
6312             ++cind;
6313             continue;
6314           }
6315           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6316         }
6317       } else {
6318         for (b = 0; b < fdof; b++) {
6319           if ((cind < fcdof) && (b == fcdofs[cind])) {
6320             ++cind;
6321             continue;
6322           }
6323           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6324         }
6325       }
6326     } else {
6327       if (perm) {
6328         for (b = 0; b < fdof; b++) {
6329           if ((cind < fcdof) && (b == fcdofs[cind])) {
6330             ++cind;
6331             continue;
6332           }
6333           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6334         }
6335       } else {
6336         for (b = 0; b < fdof; b++) {
6337           if ((cind < fcdof) && (b == fcdofs[cind])) {
6338             ++cind;
6339             continue;
6340           }
6341           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6342         }
6343       }
6344     }
6345   }
6346   *offset += fdof;
6347   PetscFunctionReturn(0);
6348 }
6349 
6350 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[])
6351 {
6352   PetscScalar    *a;
6353   PetscInt        fdof, foff, fcdof, foffset = *offset;
6354   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6355   PetscInt        Nc, cind = 0, ncind = 0, b;
6356   PetscBool       ncSet, fcSet;
6357 
6358   PetscFunctionBegin;
6359   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6360   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6361   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6362   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6363   a = &array[foff];
6364   if (fcdof) {
6365     /* We just override fcdof and fcdofs with Ncc and comps */
6366     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6367     if (clperm) {
6368       if (perm) {
6369         if (comps) {
6370           for (b = 0; b < fdof; b++) {
6371             ncSet = fcSet = PETSC_FALSE;
6372             if (b % Nc == comps[ncind]) {
6373               ncind = (ncind + 1) % Ncc;
6374               ncSet = PETSC_TRUE;
6375             }
6376             if ((cind < fcdof) && (b == fcdofs[cind])) {
6377               ++cind;
6378               fcSet = PETSC_TRUE;
6379             }
6380             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6381           }
6382         } else {
6383           for (b = 0; b < fdof; b++) {
6384             if ((cind < fcdof) && (b == fcdofs[cind])) {
6385               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6386               ++cind;
6387             }
6388           }
6389         }
6390       } else {
6391         if (comps) {
6392           for (b = 0; b < fdof; b++) {
6393             ncSet = fcSet = PETSC_FALSE;
6394             if (b % Nc == comps[ncind]) {
6395               ncind = (ncind + 1) % Ncc;
6396               ncSet = PETSC_TRUE;
6397             }
6398             if ((cind < fcdof) && (b == fcdofs[cind])) {
6399               ++cind;
6400               fcSet = PETSC_TRUE;
6401             }
6402             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6403           }
6404         } else {
6405           for (b = 0; b < fdof; b++) {
6406             if ((cind < fcdof) && (b == fcdofs[cind])) {
6407               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6408               ++cind;
6409             }
6410           }
6411         }
6412       }
6413     } else {
6414       if (perm) {
6415         if (comps) {
6416           for (b = 0; b < fdof; b++) {
6417             ncSet = fcSet = PETSC_FALSE;
6418             if (b % Nc == comps[ncind]) {
6419               ncind = (ncind + 1) % Ncc;
6420               ncSet = PETSC_TRUE;
6421             }
6422             if ((cind < fcdof) && (b == fcdofs[cind])) {
6423               ++cind;
6424               fcSet = PETSC_TRUE;
6425             }
6426             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6427           }
6428         } else {
6429           for (b = 0; b < fdof; b++) {
6430             if ((cind < fcdof) && (b == fcdofs[cind])) {
6431               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6432               ++cind;
6433             }
6434           }
6435         }
6436       } else {
6437         if (comps) {
6438           for (b = 0; b < fdof; b++) {
6439             ncSet = fcSet = PETSC_FALSE;
6440             if (b % Nc == comps[ncind]) {
6441               ncind = (ncind + 1) % Ncc;
6442               ncSet = PETSC_TRUE;
6443             }
6444             if ((cind < fcdof) && (b == fcdofs[cind])) {
6445               ++cind;
6446               fcSet = PETSC_TRUE;
6447             }
6448             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6449           }
6450         } else {
6451           for (b = 0; b < fdof; b++) {
6452             if ((cind < fcdof) && (b == fcdofs[cind])) {
6453               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6454               ++cind;
6455             }
6456           }
6457         }
6458       }
6459     }
6460   }
6461   *offset += fdof;
6462   PetscFunctionReturn(0);
6463 }
6464 
6465 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6466 {
6467   PetscScalar    *array;
6468   const PetscInt *cone, *coneO;
6469   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6470 
6471   PetscFunctionBeginHot;
6472   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6473   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6474   PetscCall(DMPlexGetCone(dm, point, &cone));
6475   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6476   PetscCall(VecGetArray(v, &array));
6477   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6478     const PetscInt cp = !p ? point : cone[p - 1];
6479     const PetscInt o  = !p ? 0 : coneO[p - 1];
6480 
6481     if ((cp < pStart) || (cp >= pEnd)) {
6482       dof = 0;
6483       continue;
6484     }
6485     PetscCall(PetscSectionGetDof(section, cp, &dof));
6486     /* ADD_VALUES */
6487     {
6488       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6489       PetscScalar    *a;
6490       PetscInt        cdof, coff, cind = 0, k;
6491 
6492       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6493       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6494       a = &array[coff];
6495       if (!cdof) {
6496         if (o >= 0) {
6497           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6498         } else {
6499           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6500         }
6501       } else {
6502         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6503         if (o >= 0) {
6504           for (k = 0; k < dof; ++k) {
6505             if ((cind < cdof) && (k == cdofs[cind])) {
6506               ++cind;
6507               continue;
6508             }
6509             a[k] += values[off + k];
6510           }
6511         } else {
6512           for (k = 0; k < dof; ++k) {
6513             if ((cind < cdof) && (k == cdofs[cind])) {
6514               ++cind;
6515               continue;
6516             }
6517             a[k] += values[off + dof - k - 1];
6518           }
6519         }
6520       }
6521     }
6522   }
6523   PetscCall(VecRestoreArray(v, &array));
6524   PetscFunctionReturn(0);
6525 }
6526 
6527 /*@C
6528   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6529 
6530   Not collective
6531 
6532   Input Parameters:
6533 + dm - The `DM`
6534 . section - The section describing the layout in v, or NULL to use the default section
6535 . v - The local vector
6536 . point - The point in the DM
6537 . values - The array of values
6538 - mode - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6539          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6540 
6541   Level: intermediate
6542 
6543 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6544 @*/
6545 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6546 {
6547   PetscSection    clSection;
6548   IS              clPoints;
6549   PetscScalar    *array;
6550   PetscInt       *points = NULL;
6551   const PetscInt *clp, *clperm = NULL;
6552   PetscInt        depth, numFields, numPoints, p, clsize;
6553 
6554   PetscFunctionBeginHot;
6555   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6556   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6557   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6558   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6559   PetscCall(DMPlexGetDepth(dm, &depth));
6560   PetscCall(PetscSectionGetNumFields(section, &numFields));
6561   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6562     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6563     PetscFunctionReturn(0);
6564   }
6565   /* Get points */
6566   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6567   for (clsize = 0, p = 0; p < numPoints; p++) {
6568     PetscInt dof;
6569     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6570     clsize += dof;
6571   }
6572   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6573   /* Get array */
6574   PetscCall(VecGetArray(v, &array));
6575   /* Get values */
6576   if (numFields > 0) {
6577     PetscInt offset = 0, f;
6578     for (f = 0; f < numFields; ++f) {
6579       const PetscInt    **perms = NULL;
6580       const PetscScalar **flips = NULL;
6581 
6582       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6583       switch (mode) {
6584       case INSERT_VALUES:
6585         for (p = 0; p < numPoints; p++) {
6586           const PetscInt     point = points[2 * p];
6587           const PetscInt    *perm  = perms ? perms[p] : NULL;
6588           const PetscScalar *flip  = flips ? flips[p] : NULL;
6589           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6590         }
6591         break;
6592       case INSERT_ALL_VALUES:
6593         for (p = 0; p < numPoints; p++) {
6594           const PetscInt     point = points[2 * p];
6595           const PetscInt    *perm  = perms ? perms[p] : NULL;
6596           const PetscScalar *flip  = flips ? flips[p] : NULL;
6597           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6598         }
6599         break;
6600       case INSERT_BC_VALUES:
6601         for (p = 0; p < numPoints; p++) {
6602           const PetscInt     point = points[2 * p];
6603           const PetscInt    *perm  = perms ? perms[p] : NULL;
6604           const PetscScalar *flip  = flips ? flips[p] : NULL;
6605           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6606         }
6607         break;
6608       case ADD_VALUES:
6609         for (p = 0; p < numPoints; p++) {
6610           const PetscInt     point = points[2 * p];
6611           const PetscInt    *perm  = perms ? perms[p] : NULL;
6612           const PetscScalar *flip  = flips ? flips[p] : NULL;
6613           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6614         }
6615         break;
6616       case ADD_ALL_VALUES:
6617         for (p = 0; p < numPoints; p++) {
6618           const PetscInt     point = points[2 * p];
6619           const PetscInt    *perm  = perms ? perms[p] : NULL;
6620           const PetscScalar *flip  = flips ? flips[p] : NULL;
6621           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6622         }
6623         break;
6624       case ADD_BC_VALUES:
6625         for (p = 0; p < numPoints; p++) {
6626           const PetscInt     point = points[2 * p];
6627           const PetscInt    *perm  = perms ? perms[p] : NULL;
6628           const PetscScalar *flip  = flips ? flips[p] : NULL;
6629           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6630         }
6631         break;
6632       default:
6633         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6634       }
6635       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6636     }
6637   } else {
6638     PetscInt            dof, off;
6639     const PetscInt    **perms = NULL;
6640     const PetscScalar **flips = NULL;
6641 
6642     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6643     switch (mode) {
6644     case INSERT_VALUES:
6645       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6646         const PetscInt     point = points[2 * p];
6647         const PetscInt    *perm  = perms ? perms[p] : NULL;
6648         const PetscScalar *flip  = flips ? flips[p] : NULL;
6649         PetscCall(PetscSectionGetDof(section, point, &dof));
6650         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6651       }
6652       break;
6653     case INSERT_ALL_VALUES:
6654       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6655         const PetscInt     point = points[2 * p];
6656         const PetscInt    *perm  = perms ? perms[p] : NULL;
6657         const PetscScalar *flip  = flips ? flips[p] : NULL;
6658         PetscCall(PetscSectionGetDof(section, point, &dof));
6659         updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array);
6660       }
6661       break;
6662     case INSERT_BC_VALUES:
6663       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6664         const PetscInt     point = points[2 * p];
6665         const PetscInt    *perm  = perms ? perms[p] : NULL;
6666         const PetscScalar *flip  = flips ? flips[p] : NULL;
6667         PetscCall(PetscSectionGetDof(section, point, &dof));
6668         updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array);
6669       }
6670       break;
6671     case ADD_VALUES:
6672       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6673         const PetscInt     point = points[2 * p];
6674         const PetscInt    *perm  = perms ? perms[p] : NULL;
6675         const PetscScalar *flip  = flips ? flips[p] : NULL;
6676         PetscCall(PetscSectionGetDof(section, point, &dof));
6677         updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array);
6678       }
6679       break;
6680     case ADD_ALL_VALUES:
6681       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6682         const PetscInt     point = points[2 * p];
6683         const PetscInt    *perm  = perms ? perms[p] : NULL;
6684         const PetscScalar *flip  = flips ? flips[p] : NULL;
6685         PetscCall(PetscSectionGetDof(section, point, &dof));
6686         updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array);
6687       }
6688       break;
6689     case ADD_BC_VALUES:
6690       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6691         const PetscInt     point = points[2 * p];
6692         const PetscInt    *perm  = perms ? perms[p] : NULL;
6693         const PetscScalar *flip  = flips ? flips[p] : NULL;
6694         PetscCall(PetscSectionGetDof(section, point, &dof));
6695         updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array);
6696       }
6697       break;
6698     default:
6699       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6700     }
6701     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6702   }
6703   /* Cleanup points */
6704   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6705   /* Cleanup array */
6706   PetscCall(VecRestoreArray(v, &array));
6707   PetscFunctionReturn(0);
6708 }
6709 
6710 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6711 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6712 {
6713   PetscFunctionBegin;
6714   *contains = PETSC_TRUE;
6715   if (label) {
6716     PetscInt fdof;
6717 
6718     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6719     if (!*contains) {
6720       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6721       *offset += fdof;
6722       PetscFunctionReturn(0);
6723     }
6724   }
6725   PetscFunctionReturn(0);
6726 }
6727 
6728 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6729 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)
6730 {
6731   PetscSection    clSection;
6732   IS              clPoints;
6733   PetscScalar    *array;
6734   PetscInt       *points = NULL;
6735   const PetscInt *clp;
6736   PetscInt        numFields, numPoints, p;
6737   PetscInt        offset = 0, f;
6738 
6739   PetscFunctionBeginHot;
6740   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6741   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6742   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6743   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6744   PetscCall(PetscSectionGetNumFields(section, &numFields));
6745   /* Get points */
6746   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6747   /* Get array */
6748   PetscCall(VecGetArray(v, &array));
6749   /* Get values */
6750   for (f = 0; f < numFields; ++f) {
6751     const PetscInt    **perms = NULL;
6752     const PetscScalar **flips = NULL;
6753     PetscBool           contains;
6754 
6755     if (!fieldActive[f]) {
6756       for (p = 0; p < numPoints * 2; p += 2) {
6757         PetscInt fdof;
6758         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6759         offset += fdof;
6760       }
6761       continue;
6762     }
6763     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6764     switch (mode) {
6765     case INSERT_VALUES:
6766       for (p = 0; p < numPoints; p++) {
6767         const PetscInt     point = points[2 * p];
6768         const PetscInt    *perm  = perms ? perms[p] : NULL;
6769         const PetscScalar *flip  = flips ? flips[p] : NULL;
6770         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6771         if (!contains) continue;
6772         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6773       }
6774       break;
6775     case INSERT_ALL_VALUES:
6776       for (p = 0; p < numPoints; p++) {
6777         const PetscInt     point = points[2 * p];
6778         const PetscInt    *perm  = perms ? perms[p] : NULL;
6779         const PetscScalar *flip  = flips ? flips[p] : NULL;
6780         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6781         if (!contains) continue;
6782         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6783       }
6784       break;
6785     case INSERT_BC_VALUES:
6786       for (p = 0; p < numPoints; p++) {
6787         const PetscInt     point = points[2 * p];
6788         const PetscInt    *perm  = perms ? perms[p] : NULL;
6789         const PetscScalar *flip  = flips ? flips[p] : NULL;
6790         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6791         if (!contains) continue;
6792         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6793       }
6794       break;
6795     case ADD_VALUES:
6796       for (p = 0; p < numPoints; p++) {
6797         const PetscInt     point = points[2 * p];
6798         const PetscInt    *perm  = perms ? perms[p] : NULL;
6799         const PetscScalar *flip  = flips ? flips[p] : NULL;
6800         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6801         if (!contains) continue;
6802         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6803       }
6804       break;
6805     case ADD_ALL_VALUES:
6806       for (p = 0; p < numPoints; p++) {
6807         const PetscInt     point = points[2 * p];
6808         const PetscInt    *perm  = perms ? perms[p] : NULL;
6809         const PetscScalar *flip  = flips ? flips[p] : NULL;
6810         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6811         if (!contains) continue;
6812         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6813       }
6814       break;
6815     default:
6816       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6817     }
6818     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6819   }
6820   /* Cleanup points */
6821   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6822   /* Cleanup array */
6823   PetscCall(VecRestoreArray(v, &array));
6824   PetscFunctionReturn(0);
6825 }
6826 
6827 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6828 {
6829   PetscMPIInt rank;
6830   PetscInt    i, j;
6831 
6832   PetscFunctionBegin;
6833   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6834   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6835   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6836   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6837   numCIndices = numCIndices ? numCIndices : numRIndices;
6838   if (!values) PetscFunctionReturn(0);
6839   for (i = 0; i < numRIndices; i++) {
6840     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6841     for (j = 0; j < numCIndices; j++) {
6842 #if defined(PETSC_USE_COMPLEX)
6843       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6844 #else
6845       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6846 #endif
6847     }
6848     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6849   }
6850   PetscFunctionReturn(0);
6851 }
6852 
6853 /*
6854   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6855 
6856   Input Parameters:
6857 + section - The section for this data layout
6858 . islocal - Is the section (and thus indices being requested) local or global?
6859 . point   - The point contributing dofs with these indices
6860 . off     - The global offset of this point
6861 . loff    - The local offset of each field
6862 . setBC   - The flag determining whether to include indices of boundary values
6863 . perm    - A permutation of the dofs on this point, or NULL
6864 - indperm - A permutation of the entire indices array, or NULL
6865 
6866   Output Parameter:
6867 . indices - Indices for dofs on this point
6868 
6869   Level: developer
6870 
6871   Note: The indices could be local or global, depending on the value of 'off'.
6872 */
6873 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6874 {
6875   PetscInt        dof;   /* The number of unknowns on this point */
6876   PetscInt        cdof;  /* The number of constraints on this point */
6877   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6878   PetscInt        cind = 0, k;
6879 
6880   PetscFunctionBegin;
6881   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6882   PetscCall(PetscSectionGetDof(section, point, &dof));
6883   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6884   if (!cdof || setBC) {
6885     for (k = 0; k < dof; ++k) {
6886       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6887       const PetscInt ind    = indperm ? indperm[preind] : preind;
6888 
6889       indices[ind] = off + k;
6890     }
6891   } else {
6892     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6893     for (k = 0; k < dof; ++k) {
6894       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6895       const PetscInt ind    = indperm ? indperm[preind] : preind;
6896 
6897       if ((cind < cdof) && (k == cdofs[cind])) {
6898         /* Insert check for returning constrained indices */
6899         indices[ind] = -(off + k + 1);
6900         ++cind;
6901       } else {
6902         indices[ind] = off + k - (islocal ? 0 : cind);
6903       }
6904     }
6905   }
6906   *loff += dof;
6907   PetscFunctionReturn(0);
6908 }
6909 
6910 /*
6911  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6912 
6913  Input Parameters:
6914 + section - a section (global or local)
6915 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6916 . point - point within section
6917 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6918 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6919 . setBC - identify constrained (boundary condition) points via involution.
6920 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6921 . permsoff - offset
6922 - indperm - index permutation
6923 
6924  Output Parameter:
6925 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6926 . indices - array to hold indices (as defined by section) of each dof associated with point
6927 
6928  Notes:
6929  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6930  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6931  in the local vector.
6932 
6933  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6934  significant).  It is invalid to call with a global section and setBC=true.
6935 
6936  Developer Note:
6937  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6938  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6939  offset could be obtained from the section instead of passing it explicitly as we do now.
6940 
6941  Example:
6942  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6943  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6944  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6945  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.
6946 
6947  Level: developer
6948 */
6949 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[])
6950 {
6951   PetscInt numFields, foff, f;
6952 
6953   PetscFunctionBegin;
6954   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6955   PetscCall(PetscSectionGetNumFields(section, &numFields));
6956   for (f = 0, foff = 0; f < numFields; ++f) {
6957     PetscInt        fdof, cfdof;
6958     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6959     PetscInt        cind = 0, b;
6960     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6961 
6962     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6963     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6964     if (!cfdof || setBC) {
6965       for (b = 0; b < fdof; ++b) {
6966         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6967         const PetscInt ind    = indperm ? indperm[preind] : preind;
6968 
6969         indices[ind] = off + foff + b;
6970       }
6971     } else {
6972       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6973       for (b = 0; b < fdof; ++b) {
6974         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6975         const PetscInt ind    = indperm ? indperm[preind] : preind;
6976 
6977         if ((cind < cfdof) && (b == fcdofs[cind])) {
6978           indices[ind] = -(off + foff + b + 1);
6979           ++cind;
6980         } else {
6981           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6982         }
6983       }
6984     }
6985     foff += (setBC || islocal ? fdof : (fdof - cfdof));
6986     foffs[f] += fdof;
6987   }
6988   PetscFunctionReturn(0);
6989 }
6990 
6991 /*
6992   This version believes the globalSection offsets for each field, rather than just the point offset
6993 
6994  . foffs - The offset into 'indices' for each field, since it is segregated by field
6995 
6996  Notes:
6997  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6998  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6999 */
7000 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7001 {
7002   PetscInt numFields, foff, f;
7003 
7004   PetscFunctionBegin;
7005   PetscCall(PetscSectionGetNumFields(section, &numFields));
7006   for (f = 0; f < numFields; ++f) {
7007     PetscInt        fdof, cfdof;
7008     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7009     PetscInt        cind = 0, b;
7010     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7011 
7012     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7013     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7014     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7015     if (!cfdof) {
7016       for (b = 0; b < fdof; ++b) {
7017         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7018         const PetscInt ind    = indperm ? indperm[preind] : preind;
7019 
7020         indices[ind] = foff + b;
7021       }
7022     } else {
7023       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7024       for (b = 0; b < fdof; ++b) {
7025         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7026         const PetscInt ind    = indperm ? indperm[preind] : preind;
7027 
7028         if ((cind < cfdof) && (b == fcdofs[cind])) {
7029           indices[ind] = -(foff + b + 1);
7030           ++cind;
7031         } else {
7032           indices[ind] = foff + b - cind;
7033         }
7034       }
7035     }
7036     foffs[f] += fdof;
7037   }
7038   PetscFunctionReturn(0);
7039 }
7040 
7041 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)
7042 {
7043   Mat             cMat;
7044   PetscSection    aSec, cSec;
7045   IS              aIS;
7046   PetscInt        aStart = -1, aEnd = -1;
7047   const PetscInt *anchors;
7048   PetscInt        numFields, f, p, q, newP = 0;
7049   PetscInt        newNumPoints = 0, newNumIndices = 0;
7050   PetscInt       *newPoints, *indices, *newIndices;
7051   PetscInt        maxAnchor, maxDof;
7052   PetscInt        newOffsets[32];
7053   PetscInt       *pointMatOffsets[32];
7054   PetscInt       *newPointOffsets[32];
7055   PetscScalar    *pointMat[32];
7056   PetscScalar    *newValues      = NULL, *tmpValues;
7057   PetscBool       anyConstrained = PETSC_FALSE;
7058 
7059   PetscFunctionBegin;
7060   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7061   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7062   PetscCall(PetscSectionGetNumFields(section, &numFields));
7063 
7064   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7065   /* if there are point-to-point constraints */
7066   if (aSec) {
7067     PetscCall(PetscArrayzero(newOffsets, 32));
7068     PetscCall(ISGetIndices(aIS, &anchors));
7069     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7070     /* figure out how many points are going to be in the new element matrix
7071      * (we allow double counting, because it's all just going to be summed
7072      * into the global matrix anyway) */
7073     for (p = 0; p < 2 * numPoints; p += 2) {
7074       PetscInt b    = points[p];
7075       PetscInt bDof = 0, bSecDof;
7076 
7077       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7078       if (!bSecDof) continue;
7079       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7080       if (bDof) {
7081         /* this point is constrained */
7082         /* it is going to be replaced by its anchors */
7083         PetscInt bOff, q;
7084 
7085         anyConstrained = PETSC_TRUE;
7086         newNumPoints += bDof;
7087         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7088         for (q = 0; q < bDof; q++) {
7089           PetscInt a = anchors[bOff + q];
7090           PetscInt aDof;
7091 
7092           PetscCall(PetscSectionGetDof(section, a, &aDof));
7093           newNumIndices += aDof;
7094           for (f = 0; f < numFields; ++f) {
7095             PetscInt fDof;
7096 
7097             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7098             newOffsets[f + 1] += fDof;
7099           }
7100         }
7101       } else {
7102         /* this point is not constrained */
7103         newNumPoints++;
7104         newNumIndices += bSecDof;
7105         for (f = 0; f < numFields; ++f) {
7106           PetscInt fDof;
7107 
7108           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7109           newOffsets[f + 1] += fDof;
7110         }
7111       }
7112     }
7113   }
7114   if (!anyConstrained) {
7115     if (outNumPoints) *outNumPoints = 0;
7116     if (outNumIndices) *outNumIndices = 0;
7117     if (outPoints) *outPoints = NULL;
7118     if (outValues) *outValues = NULL;
7119     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7120     PetscFunctionReturn(0);
7121   }
7122 
7123   if (outNumPoints) *outNumPoints = newNumPoints;
7124   if (outNumIndices) *outNumIndices = newNumIndices;
7125 
7126   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7127 
7128   if (!outPoints && !outValues) {
7129     if (offsets) {
7130       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7131     }
7132     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7133     PetscFunctionReturn(0);
7134   }
7135 
7136   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7137 
7138   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7139 
7140   /* workspaces */
7141   if (numFields) {
7142     for (f = 0; f < numFields; f++) {
7143       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7144       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7145     }
7146   } else {
7147     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7148     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7149   }
7150 
7151   /* get workspaces for the point-to-point matrices */
7152   if (numFields) {
7153     PetscInt totalOffset, totalMatOffset;
7154 
7155     for (p = 0; p < numPoints; p++) {
7156       PetscInt b    = points[2 * p];
7157       PetscInt bDof = 0, bSecDof;
7158 
7159       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7160       if (!bSecDof) {
7161         for (f = 0; f < numFields; f++) {
7162           newPointOffsets[f][p + 1] = 0;
7163           pointMatOffsets[f][p + 1] = 0;
7164         }
7165         continue;
7166       }
7167       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7168       if (bDof) {
7169         for (f = 0; f < numFields; f++) {
7170           PetscInt fDof, q, bOff, allFDof = 0;
7171 
7172           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7173           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7174           for (q = 0; q < bDof; q++) {
7175             PetscInt a = anchors[bOff + q];
7176             PetscInt aFDof;
7177 
7178             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7179             allFDof += aFDof;
7180           }
7181           newPointOffsets[f][p + 1] = allFDof;
7182           pointMatOffsets[f][p + 1] = fDof * allFDof;
7183         }
7184       } else {
7185         for (f = 0; f < numFields; f++) {
7186           PetscInt fDof;
7187 
7188           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7189           newPointOffsets[f][p + 1] = fDof;
7190           pointMatOffsets[f][p + 1] = 0;
7191         }
7192       }
7193     }
7194     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7195       newPointOffsets[f][0] = totalOffset;
7196       pointMatOffsets[f][0] = totalMatOffset;
7197       for (p = 0; p < numPoints; p++) {
7198         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7199         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7200       }
7201       totalOffset    = newPointOffsets[f][numPoints];
7202       totalMatOffset = pointMatOffsets[f][numPoints];
7203       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7204     }
7205   } else {
7206     for (p = 0; p < numPoints; p++) {
7207       PetscInt b    = points[2 * p];
7208       PetscInt bDof = 0, bSecDof;
7209 
7210       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7211       if (!bSecDof) {
7212         newPointOffsets[0][p + 1] = 0;
7213         pointMatOffsets[0][p + 1] = 0;
7214         continue;
7215       }
7216       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7217       if (bDof) {
7218         PetscInt bOff, q, allDof = 0;
7219 
7220         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7221         for (q = 0; q < bDof; q++) {
7222           PetscInt a = anchors[bOff + q], aDof;
7223 
7224           PetscCall(PetscSectionGetDof(section, a, &aDof));
7225           allDof += aDof;
7226         }
7227         newPointOffsets[0][p + 1] = allDof;
7228         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7229       } else {
7230         newPointOffsets[0][p + 1] = bSecDof;
7231         pointMatOffsets[0][p + 1] = 0;
7232       }
7233     }
7234     newPointOffsets[0][0] = 0;
7235     pointMatOffsets[0][0] = 0;
7236     for (p = 0; p < numPoints; p++) {
7237       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7238       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7239     }
7240     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7241   }
7242 
7243   /* output arrays */
7244   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7245 
7246   /* get the point-to-point matrices; construct newPoints */
7247   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7248   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7249   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7250   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7251   if (numFields) {
7252     for (p = 0, newP = 0; p < numPoints; p++) {
7253       PetscInt b    = points[2 * p];
7254       PetscInt o    = points[2 * p + 1];
7255       PetscInt bDof = 0, bSecDof;
7256 
7257       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7258       if (!bSecDof) continue;
7259       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7260       if (bDof) {
7261         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7262 
7263         fStart[0] = 0;
7264         fEnd[0]   = 0;
7265         for (f = 0; f < numFields; f++) {
7266           PetscInt fDof;
7267 
7268           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7269           fStart[f + 1] = fStart[f] + fDof;
7270           fEnd[f + 1]   = fStart[f + 1];
7271         }
7272         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7273         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7274 
7275         fAnchorStart[0] = 0;
7276         fAnchorEnd[0]   = 0;
7277         for (f = 0; f < numFields; f++) {
7278           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7279 
7280           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7281           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7282         }
7283         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7284         for (q = 0; q < bDof; q++) {
7285           PetscInt a = anchors[bOff + q], aOff;
7286 
7287           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7288           newPoints[2 * (newP + q)]     = a;
7289           newPoints[2 * (newP + q) + 1] = 0;
7290           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7291           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7292         }
7293         newP += bDof;
7294 
7295         if (outValues) {
7296           /* get the point-to-point submatrix */
7297           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]));
7298         }
7299       } else {
7300         newPoints[2 * newP]     = b;
7301         newPoints[2 * newP + 1] = o;
7302         newP++;
7303       }
7304     }
7305   } else {
7306     for (p = 0; p < numPoints; p++) {
7307       PetscInt b    = points[2 * p];
7308       PetscInt o    = points[2 * p + 1];
7309       PetscInt bDof = 0, bSecDof;
7310 
7311       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7312       if (!bSecDof) continue;
7313       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7314       if (bDof) {
7315         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7316 
7317         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7318         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7319 
7320         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7321         for (q = 0; q < bDof; q++) {
7322           PetscInt a = anchors[bOff + q], aOff;
7323 
7324           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7325 
7326           newPoints[2 * (newP + q)]     = a;
7327           newPoints[2 * (newP + q) + 1] = 0;
7328           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7329           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7330         }
7331         newP += bDof;
7332 
7333         /* get the point-to-point submatrix */
7334         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7335       } else {
7336         newPoints[2 * newP]     = b;
7337         newPoints[2 * newP + 1] = o;
7338         newP++;
7339       }
7340     }
7341   }
7342 
7343   if (outValues) {
7344     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7345     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7346     /* multiply constraints on the right */
7347     if (numFields) {
7348       for (f = 0; f < numFields; f++) {
7349         PetscInt oldOff = offsets[f];
7350 
7351         for (p = 0; p < numPoints; p++) {
7352           PetscInt cStart = newPointOffsets[f][p];
7353           PetscInt b      = points[2 * p];
7354           PetscInt c, r, k;
7355           PetscInt dof;
7356 
7357           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7358           if (!dof) continue;
7359           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7360             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7361             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7362 
7363             for (r = 0; r < numIndices; r++) {
7364               for (c = 0; c < nCols; c++) {
7365                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7366               }
7367             }
7368           } else {
7369             /* copy this column as is */
7370             for (r = 0; r < numIndices; r++) {
7371               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7372             }
7373           }
7374           oldOff += dof;
7375         }
7376       }
7377     } else {
7378       PetscInt oldOff = 0;
7379       for (p = 0; p < numPoints; p++) {
7380         PetscInt cStart = newPointOffsets[0][p];
7381         PetscInt b      = points[2 * p];
7382         PetscInt c, r, k;
7383         PetscInt dof;
7384 
7385         PetscCall(PetscSectionGetDof(section, b, &dof));
7386         if (!dof) continue;
7387         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7388           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7389           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7390 
7391           for (r = 0; r < numIndices; r++) {
7392             for (c = 0; c < nCols; c++) {
7393               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7394             }
7395           }
7396         } else {
7397           /* copy this column as is */
7398           for (r = 0; r < numIndices; r++) {
7399             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7400           }
7401         }
7402         oldOff += dof;
7403       }
7404     }
7405 
7406     if (multiplyLeft) {
7407       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7408       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7409       /* multiply constraints transpose on the left */
7410       if (numFields) {
7411         for (f = 0; f < numFields; f++) {
7412           PetscInt oldOff = offsets[f];
7413 
7414           for (p = 0; p < numPoints; p++) {
7415             PetscInt rStart = newPointOffsets[f][p];
7416             PetscInt b      = points[2 * p];
7417             PetscInt c, r, k;
7418             PetscInt dof;
7419 
7420             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7421             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7422               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7423               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7424 
7425               for (r = 0; r < nRows; r++) {
7426                 for (c = 0; c < newNumIndices; c++) {
7427                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7428                 }
7429               }
7430             } else {
7431               /* copy this row as is */
7432               for (r = 0; r < dof; r++) {
7433                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7434               }
7435             }
7436             oldOff += dof;
7437           }
7438         }
7439       } else {
7440         PetscInt oldOff = 0;
7441 
7442         for (p = 0; p < numPoints; p++) {
7443           PetscInt rStart = newPointOffsets[0][p];
7444           PetscInt b      = points[2 * p];
7445           PetscInt c, r, k;
7446           PetscInt dof;
7447 
7448           PetscCall(PetscSectionGetDof(section, b, &dof));
7449           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7450             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7451             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7452 
7453             for (r = 0; r < nRows; r++) {
7454               for (c = 0; c < newNumIndices; c++) {
7455                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7456               }
7457             }
7458           } else {
7459             /* copy this row as is */
7460             for (r = 0; r < dof; r++) {
7461               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7462             }
7463           }
7464           oldOff += dof;
7465         }
7466       }
7467 
7468       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7469     } else {
7470       newValues = tmpValues;
7471     }
7472   }
7473 
7474   /* clean up */
7475   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7476   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7477 
7478   if (numFields) {
7479     for (f = 0; f < numFields; f++) {
7480       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7481       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7482       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7483     }
7484   } else {
7485     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7486     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7487     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7488   }
7489   PetscCall(ISRestoreIndices(aIS, &anchors));
7490 
7491   /* output */
7492   if (outPoints) {
7493     *outPoints = newPoints;
7494   } else {
7495     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7496   }
7497   if (outValues) *outValues = newValues;
7498   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7499   PetscFunctionReturn(0);
7500 }
7501 
7502 /*@C
7503   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7504 
7505   Not collective
7506 
7507   Input Parameters:
7508 + dm         - The `DM`
7509 . section    - The `PetscSection` describing the points (a local section)
7510 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7511 . point      - The point defining the closure
7512 - useClPerm  - Use the closure point permutation if available
7513 
7514   Output Parameters:
7515 + numIndices - The number of dof indices in the closure of point with the input sections
7516 . indices    - The dof indices
7517 . outOffsets - Array to write the field offsets into, or NULL
7518 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7519 
7520   Level: advanced
7521 
7522   Notes:
7523   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7524 
7525   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7526   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7527   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7528   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7529   indices (with the above semantics) are implied.
7530 
7531 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7532           `PetscSection`, `DMGetGlobalSection()`
7533 @*/
7534 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7535 {
7536   /* Closure ordering */
7537   PetscSection    clSection;
7538   IS              clPoints;
7539   const PetscInt *clp;
7540   PetscInt       *points;
7541   const PetscInt *clperm = NULL;
7542   /* Dof permutation and sign flips */
7543   const PetscInt    **perms[32] = {NULL};
7544   const PetscScalar **flips[32] = {NULL};
7545   PetscScalar        *valCopy   = NULL;
7546   /* Hanging node constraints */
7547   PetscInt    *pointsC = NULL;
7548   PetscScalar *valuesC = NULL;
7549   PetscInt     NclC, NiC;
7550 
7551   PetscInt *idx;
7552   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7553   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7554 
7555   PetscFunctionBeginHot;
7556   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7557   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7558   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7559   if (numIndices) PetscValidIntPointer(numIndices, 6);
7560   if (indices) PetscValidPointer(indices, 7);
7561   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7562   if (values) PetscValidPointer(values, 9);
7563   PetscCall(PetscSectionGetNumFields(section, &Nf));
7564   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7565   PetscCall(PetscArrayzero(offsets, 32));
7566   /* 1) Get points in closure */
7567   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7568   if (useClPerm) {
7569     PetscInt depth, clsize;
7570     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7571     for (clsize = 0, p = 0; p < Ncl; p++) {
7572       PetscInt dof;
7573       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7574       clsize += dof;
7575     }
7576     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7577   }
7578   /* 2) Get number of indices on these points and field offsets from section */
7579   for (p = 0; p < Ncl * 2; p += 2) {
7580     PetscInt dof, fdof;
7581 
7582     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7583     for (f = 0; f < Nf; ++f) {
7584       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7585       offsets[f + 1] += fdof;
7586     }
7587     Ni += dof;
7588   }
7589   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7590   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7591   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7592   for (f = 0; f < PetscMax(1, Nf); ++f) {
7593     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7594     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7595     /* may need to apply sign changes to the element matrix */
7596     if (values && flips[f]) {
7597       PetscInt foffset = offsets[f];
7598 
7599       for (p = 0; p < Ncl; ++p) {
7600         PetscInt           pnt  = points[2 * p], fdof;
7601         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7602 
7603         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7604         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7605         if (flip) {
7606           PetscInt i, j, k;
7607 
7608           if (!valCopy) {
7609             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7610             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7611             *values = valCopy;
7612           }
7613           for (i = 0; i < fdof; ++i) {
7614             PetscScalar fval = flip[i];
7615 
7616             for (k = 0; k < Ni; ++k) {
7617               valCopy[Ni * (foffset + i) + k] *= fval;
7618               valCopy[Ni * k + (foffset + i)] *= fval;
7619             }
7620           }
7621         }
7622         foffset += fdof;
7623       }
7624     }
7625   }
7626   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7627   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7628   if (NclC) {
7629     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7630     for (f = 0; f < PetscMax(1, Nf); ++f) {
7631       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7632       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7633     }
7634     for (f = 0; f < PetscMax(1, Nf); ++f) {
7635       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7636       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7637     }
7638     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7639     Ncl    = NclC;
7640     Ni     = NiC;
7641     points = pointsC;
7642     if (values) *values = valuesC;
7643   }
7644   /* 5) Calculate indices */
7645   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7646   if (Nf) {
7647     PetscInt  idxOff;
7648     PetscBool useFieldOffsets;
7649 
7650     if (outOffsets) {
7651       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7652     }
7653     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7654     if (useFieldOffsets) {
7655       for (p = 0; p < Ncl; ++p) {
7656         const PetscInt pnt = points[p * 2];
7657 
7658         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7659       }
7660     } else {
7661       for (p = 0; p < Ncl; ++p) {
7662         const PetscInt pnt = points[p * 2];
7663 
7664         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7665         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7666          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7667          * global section. */
7668         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7669       }
7670     }
7671   } else {
7672     PetscInt off = 0, idxOff;
7673 
7674     for (p = 0; p < Ncl; ++p) {
7675       const PetscInt  pnt  = points[p * 2];
7676       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7677 
7678       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7679       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7680        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7681       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7682     }
7683   }
7684   /* 6) Cleanup */
7685   for (f = 0; f < PetscMax(1, Nf); ++f) {
7686     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7687     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7688   }
7689   if (NclC) {
7690     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7691   } else {
7692     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7693   }
7694 
7695   if (numIndices) *numIndices = Ni;
7696   if (indices) *indices = idx;
7697   PetscFunctionReturn(0);
7698 }
7699 
7700 /*@C
7701   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7702 
7703   Not collective
7704 
7705   Input Parameters:
7706 + dm         - The `DM`
7707 . section    - The `PetscSection` describing the points (a local section)
7708 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7709 . point      - The point defining the closure
7710 - useClPerm  - Use the closure point permutation if available
7711 
7712   Output Parameters:
7713 + numIndices - The number of dof indices in the closure of point with the input sections
7714 . indices    - The dof indices
7715 . outOffsets - Array to write the field offsets into, or NULL
7716 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7717 
7718   Level: advanced
7719 
7720   Notes:
7721   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7722 
7723   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7724   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7725   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7726   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7727   indices (with the above semantics) are implied.
7728 
7729 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7730 @*/
7731 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7732 {
7733   PetscFunctionBegin;
7734   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7735   PetscValidPointer(indices, 7);
7736   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7737   PetscFunctionReturn(0);
7738 }
7739 
7740 /*@C
7741   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7742 
7743   Not collective
7744 
7745   Input Parameters:
7746 + dm - The `DM`
7747 . section - The section describing the layout in v, or NULL to use the default section
7748 . globalSection - The section describing the layout in v, or NULL to use the default global section
7749 . A - The matrix
7750 . point - The point in the `DM`
7751 . values - The array of values
7752 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7753 
7754   Level: intermediate
7755 
7756 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7757 @*/
7758 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7759 {
7760   DM_Plex           *mesh = (DM_Plex *)dm->data;
7761   PetscInt          *indices;
7762   PetscInt           numIndices;
7763   const PetscScalar *valuesOrig = values;
7764   PetscErrorCode     ierr;
7765 
7766   PetscFunctionBegin;
7767   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7768   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7769   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7770   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7771   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7772   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7773 
7774   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7775 
7776   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7777   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7778   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7779   if (ierr) {
7780     PetscMPIInt rank;
7781 
7782     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7783     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7784     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7785     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7786     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7787     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7788   }
7789   if (mesh->printFEM > 1) {
7790     PetscInt i;
7791     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7792     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7793     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7794   }
7795 
7796   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7797   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7798   PetscFunctionReturn(0);
7799 }
7800 
7801 /*@C
7802   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7803 
7804   Not collective
7805 
7806   Input Parameters:
7807 + dmRow - The `DM` for the row fields
7808 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7809 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7810 . dmCol - The `DM` for the column fields
7811 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7812 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7813 . A - The matrix
7814 . point - The point in the `DM`
7815 . values - The array of values
7816 - mode - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7817 
7818   Level: intermediate
7819 
7820 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7821 @*/
7822 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7823 {
7824   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7825   PetscInt          *indicesRow, *indicesCol;
7826   PetscInt           numIndicesRow, numIndicesCol;
7827   const PetscScalar *valuesOrig = values;
7828   PetscErrorCode     ierr;
7829 
7830   PetscFunctionBegin;
7831   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7832   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7833   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7834   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7835   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7836   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7837   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7838   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7839   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7840   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7841   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7842 
7843   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7844   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7845 
7846   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7847   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7848   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7849   if (ierr) {
7850     PetscMPIInt rank;
7851 
7852     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7853     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7854     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7855     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7856     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7857     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7858   }
7859 
7860   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7861   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7862   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7863   PetscFunctionReturn(0);
7864 }
7865 
7866 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7867 {
7868   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7869   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7870   PetscInt       *cpoints = NULL;
7871   PetscInt       *findices, *cindices;
7872   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7873   PetscInt        foffsets[32], coffsets[32];
7874   DMPolytopeType  ct;
7875   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7876   PetscErrorCode  ierr;
7877 
7878   PetscFunctionBegin;
7879   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7880   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7881   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7882   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7883   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7884   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7885   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7886   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7887   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7888   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7889   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7890   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7891   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7892   PetscCall(PetscArrayzero(foffsets, 32));
7893   PetscCall(PetscArrayzero(coffsets, 32));
7894   /* Column indices */
7895   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7896   maxFPoints = numCPoints;
7897   /* Compress out points not in the section */
7898   /*   TODO: Squeeze out points with 0 dof as well */
7899   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7900   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7901     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7902       cpoints[q * 2]     = cpoints[p];
7903       cpoints[q * 2 + 1] = cpoints[p + 1];
7904       ++q;
7905     }
7906   }
7907   numCPoints = q;
7908   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7909     PetscInt fdof;
7910 
7911     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7912     if (!dof) continue;
7913     for (f = 0; f < numFields; ++f) {
7914       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7915       coffsets[f + 1] += fdof;
7916     }
7917     numCIndices += dof;
7918   }
7919   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7920   /* Row indices */
7921   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7922   {
7923     DMPlexTransform tr;
7924     DMPolytopeType *rct;
7925     PetscInt       *rsize, *rcone, *rornt, Nt;
7926 
7927     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7928     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7929     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7930     numSubcells = rsize[Nt - 1];
7931     PetscCall(DMPlexTransformDestroy(&tr));
7932   }
7933   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7934   for (r = 0, q = 0; r < numSubcells; ++r) {
7935     /* TODO Map from coarse to fine cells */
7936     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7937     /* Compress out points not in the section */
7938     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7939     for (p = 0; p < numFPoints * 2; p += 2) {
7940       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7941         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7942         if (!dof) continue;
7943         for (s = 0; s < q; ++s)
7944           if (fpoints[p] == ftotpoints[s * 2]) break;
7945         if (s < q) continue;
7946         ftotpoints[q * 2]     = fpoints[p];
7947         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7948         ++q;
7949       }
7950     }
7951     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7952   }
7953   numFPoints = q;
7954   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7955     PetscInt fdof;
7956 
7957     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7958     if (!dof) continue;
7959     for (f = 0; f < numFields; ++f) {
7960       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7961       foffsets[f + 1] += fdof;
7962     }
7963     numFIndices += dof;
7964   }
7965   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7966 
7967   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7968   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7969   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7970   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7971   if (numFields) {
7972     const PetscInt **permsF[32] = {NULL};
7973     const PetscInt **permsC[32] = {NULL};
7974 
7975     for (f = 0; f < numFields; f++) {
7976       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7977       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7978     }
7979     for (p = 0; p < numFPoints; p++) {
7980       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7981       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7982     }
7983     for (p = 0; p < numCPoints; p++) {
7984       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7985       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7986     }
7987     for (f = 0; f < numFields; f++) {
7988       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7989       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7990     }
7991   } else {
7992     const PetscInt **permsF = NULL;
7993     const PetscInt **permsC = NULL;
7994 
7995     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7996     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7997     for (p = 0, off = 0; p < numFPoints; p++) {
7998       const PetscInt *perm = permsF ? permsF[p] : NULL;
7999 
8000       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8001       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8002     }
8003     for (p = 0, off = 0; p < numCPoints; p++) {
8004       const PetscInt *perm = permsC ? permsC[p] : NULL;
8005 
8006       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8007       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8008     }
8009     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8010     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8011   }
8012   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8013   /* TODO: flips */
8014   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8015   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8016   if (ierr) {
8017     PetscMPIInt rank;
8018 
8019     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8020     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8021     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8022     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8023     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8024   }
8025   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8026   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8027   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8028   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8029   PetscFunctionReturn(0);
8030 }
8031 
8032 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8033 {
8034   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8035   PetscInt       *cpoints = NULL;
8036   PetscInt        foffsets[32], coffsets[32];
8037   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8038   DMPolytopeType  ct;
8039   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8040 
8041   PetscFunctionBegin;
8042   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8043   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8044   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8045   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8046   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8047   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8048   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8049   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8050   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8051   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8052   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8053   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8054   PetscCall(PetscArrayzero(foffsets, 32));
8055   PetscCall(PetscArrayzero(coffsets, 32));
8056   /* Column indices */
8057   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8058   maxFPoints = numCPoints;
8059   /* Compress out points not in the section */
8060   /*   TODO: Squeeze out points with 0 dof as well */
8061   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8062   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8063     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8064       cpoints[q * 2]     = cpoints[p];
8065       cpoints[q * 2 + 1] = cpoints[p + 1];
8066       ++q;
8067     }
8068   }
8069   numCPoints = q;
8070   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8071     PetscInt fdof;
8072 
8073     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8074     if (!dof) continue;
8075     for (f = 0; f < numFields; ++f) {
8076       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8077       coffsets[f + 1] += fdof;
8078     }
8079     numCIndices += dof;
8080   }
8081   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8082   /* Row indices */
8083   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8084   {
8085     DMPlexTransform tr;
8086     DMPolytopeType *rct;
8087     PetscInt       *rsize, *rcone, *rornt, Nt;
8088 
8089     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8090     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8091     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8092     numSubcells = rsize[Nt - 1];
8093     PetscCall(DMPlexTransformDestroy(&tr));
8094   }
8095   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8096   for (r = 0, q = 0; r < numSubcells; ++r) {
8097     /* TODO Map from coarse to fine cells */
8098     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8099     /* Compress out points not in the section */
8100     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8101     for (p = 0; p < numFPoints * 2; p += 2) {
8102       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8103         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8104         if (!dof) continue;
8105         for (s = 0; s < q; ++s)
8106           if (fpoints[p] == ftotpoints[s * 2]) break;
8107         if (s < q) continue;
8108         ftotpoints[q * 2]     = fpoints[p];
8109         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8110         ++q;
8111       }
8112     }
8113     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8114   }
8115   numFPoints = q;
8116   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8117     PetscInt fdof;
8118 
8119     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8120     if (!dof) continue;
8121     for (f = 0; f < numFields; ++f) {
8122       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8123       foffsets[f + 1] += fdof;
8124     }
8125     numFIndices += dof;
8126   }
8127   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8128 
8129   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8130   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8131   if (numFields) {
8132     const PetscInt **permsF[32] = {NULL};
8133     const PetscInt **permsC[32] = {NULL};
8134 
8135     for (f = 0; f < numFields; f++) {
8136       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8137       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8138     }
8139     for (p = 0; p < numFPoints; p++) {
8140       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8141       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8142     }
8143     for (p = 0; p < numCPoints; p++) {
8144       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8145       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8146     }
8147     for (f = 0; f < numFields; f++) {
8148       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8149       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8150     }
8151   } else {
8152     const PetscInt **permsF = NULL;
8153     const PetscInt **permsC = NULL;
8154 
8155     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8156     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8157     for (p = 0, off = 0; p < numFPoints; p++) {
8158       const PetscInt *perm = permsF ? permsF[p] : NULL;
8159 
8160       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8161       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8162     }
8163     for (p = 0, off = 0; p < numCPoints; p++) {
8164       const PetscInt *perm = permsC ? permsC[p] : NULL;
8165 
8166       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8167       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8168     }
8169     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8170     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8171   }
8172   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8173   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8174   PetscFunctionReturn(0);
8175 }
8176 
8177 /*@C
8178   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8179 
8180   Input Parameter:
8181 . dm   - The `DMPLEX` object
8182 
8183   Output Parameter:
8184 . cellHeight - The height of a cell
8185 
8186   Level: developer
8187 
8188 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`,  `DMPlexSetVTKCellHeight()`
8189 @*/
8190 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8191 {
8192   DM_Plex *mesh = (DM_Plex *)dm->data;
8193 
8194   PetscFunctionBegin;
8195   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8196   PetscValidIntPointer(cellHeight, 2);
8197   *cellHeight = mesh->vtkCellHeight;
8198   PetscFunctionReturn(0);
8199 }
8200 
8201 /*@C
8202   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8203 
8204   Input Parameters:
8205 + dm   - The `DMPLEX` object
8206 - cellHeight - The height of a cell
8207 
8208   Level: developer
8209 
8210 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8211 @*/
8212 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8213 {
8214   DM_Plex *mesh = (DM_Plex *)dm->data;
8215 
8216   PetscFunctionBegin;
8217   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8218   mesh->vtkCellHeight = cellHeight;
8219   PetscFunctionReturn(0);
8220 }
8221 
8222 /*@
8223   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
8224 
8225   Input Parameter:
8226 . dm - The `DMPLEX` object
8227 
8228   Output Parameters:
8229 + gcStart - The first ghost cell, or NULL
8230 - gcEnd   - The upper bound on ghost cells, or NULL
8231 
8232   Level: advanced
8233 
8234 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
8235 @*/
8236 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd)
8237 {
8238   DMLabel ctLabel;
8239 
8240   PetscFunctionBegin;
8241   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8242   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
8243   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
8244   // Reset label for fast lookup
8245   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
8246   PetscFunctionReturn(0);
8247 }
8248 
8249 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8250 {
8251   PetscSection section, globalSection;
8252   PetscInt    *numbers, p;
8253 
8254   PetscFunctionBegin;
8255   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8256   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8257   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8258   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8259   PetscCall(PetscSectionSetUp(section));
8260   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8261   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8262   for (p = pStart; p < pEnd; ++p) {
8263     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8264     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8265     else numbers[p - pStart] += shift;
8266   }
8267   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8268   if (globalSize) {
8269     PetscLayout layout;
8270     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8271     PetscCall(PetscLayoutGetSize(layout, globalSize));
8272     PetscCall(PetscLayoutDestroy(&layout));
8273   }
8274   PetscCall(PetscSectionDestroy(&section));
8275   PetscCall(PetscSectionDestroy(&globalSection));
8276   PetscFunctionReturn(0);
8277 }
8278 
8279 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8280 {
8281   PetscInt cellHeight, cStart, cEnd;
8282 
8283   PetscFunctionBegin;
8284   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8285   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8286   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8287   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8288   PetscFunctionReturn(0);
8289 }
8290 
8291 /*@
8292   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8293 
8294   Input Parameter:
8295 . dm   - The `DMPLEX` object
8296 
8297   Output Parameter:
8298 . globalCellNumbers - Global cell numbers for all cells on this process
8299 
8300   Level: developer
8301 
8302 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8303 @*/
8304 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8305 {
8306   DM_Plex *mesh = (DM_Plex *)dm->data;
8307 
8308   PetscFunctionBegin;
8309   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8310   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8311   *globalCellNumbers = mesh->globalCellNumbers;
8312   PetscFunctionReturn(0);
8313 }
8314 
8315 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8316 {
8317   PetscInt vStart, vEnd;
8318 
8319   PetscFunctionBegin;
8320   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8321   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8322   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8323   PetscFunctionReturn(0);
8324 }
8325 
8326 /*@
8327   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8328 
8329   Input Parameter:
8330 . dm   - The `DMPLEX` object
8331 
8332   Output Parameter:
8333 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8334 
8335   Level: developer
8336 
8337 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8338 @*/
8339 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8340 {
8341   DM_Plex *mesh = (DM_Plex *)dm->data;
8342 
8343   PetscFunctionBegin;
8344   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8345   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8346   *globalVertexNumbers = mesh->globalVertexNumbers;
8347   PetscFunctionReturn(0);
8348 }
8349 
8350 /*@
8351   DMPlexCreatePointNumbering - Create a global numbering for all points.
8352 
8353   Collective on dm
8354 
8355   Input Parameter:
8356 . dm   - The `DMPLEX` object
8357 
8358   Output Parameter:
8359 . globalPointNumbers - Global numbers for all points on this process
8360 
8361   Level: developer
8362 
8363   Notes:
8364   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8365   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8366   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8367   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8368 
8369   The partitioned mesh is
8370 ```
8371  (2)--0--(3)--1--(4)    (1)--0--(2)
8372 ```
8373   and its global numbering is
8374 ```
8375   (3)--0--(4)--1--(5)--2--(6)
8376 ```
8377   Then the global numbering is provided as
8378 ```
8379 [0] Number of indices in set 5
8380 [0] 0 0
8381 [0] 1 1
8382 [0] 2 3
8383 [0] 3 4
8384 [0] 4 -6
8385 [1] Number of indices in set 3
8386 [1] 0 2
8387 [1] 1 5
8388 [1] 2 6
8389 ```
8390 
8391 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8392 @*/
8393 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8394 {
8395   IS        nums[4];
8396   PetscInt  depths[4], gdepths[4], starts[4];
8397   PetscInt  depth, d, shift = 0;
8398   PetscBool empty = PETSC_FALSE;
8399 
8400   PetscFunctionBegin;
8401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8402   PetscCall(DMPlexGetDepth(dm, &depth));
8403   // For unstratified meshes use dim instead of depth
8404   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8405   // If any stratum is empty, we must mark all empty
8406   for (d = 0; d <= depth; ++d) {
8407     PetscInt end;
8408 
8409     depths[d] = depth - d;
8410     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8411     if (!(starts[d] - end)) empty = PETSC_TRUE;
8412   }
8413   if (empty)
8414     for (d = 0; d <= depth; ++d) {
8415       depths[d] = -1;
8416       starts[d] = -1;
8417     }
8418   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8419   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8420   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]);
8421   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8422   for (d = 0; d <= depth; ++d) {
8423     PetscInt pStart, pEnd, gsize;
8424 
8425     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8426     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8427     shift += gsize;
8428   }
8429   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8430   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8431   PetscFunctionReturn(0);
8432 }
8433 
8434 /*@
8435   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8436 
8437   Input Parameter:
8438 . dm - The `DMPLEX` object
8439 
8440   Output Parameter:
8441 . ranks - The rank field
8442 
8443   Options Database Key:
8444 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8445 
8446   Level: intermediate
8447 
8448 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8449 @*/
8450 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8451 {
8452   DM             rdm;
8453   PetscFE        fe;
8454   PetscScalar   *r;
8455   PetscMPIInt    rank;
8456   DMPolytopeType ct;
8457   PetscInt       dim, cStart, cEnd, c;
8458   PetscBool      simplex;
8459 
8460   PetscFunctionBeginUser;
8461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8462   PetscValidPointer(ranks, 2);
8463   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8464   PetscCall(DMClone(dm, &rdm));
8465   PetscCall(DMGetDimension(rdm, &dim));
8466   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8467   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8468   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8469   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8470   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8471   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8472   PetscCall(PetscFEDestroy(&fe));
8473   PetscCall(DMCreateDS(rdm));
8474   PetscCall(DMCreateGlobalVector(rdm, ranks));
8475   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8476   PetscCall(VecGetArray(*ranks, &r));
8477   for (c = cStart; c < cEnd; ++c) {
8478     PetscScalar *lr;
8479 
8480     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8481     if (lr) *lr = rank;
8482   }
8483   PetscCall(VecRestoreArray(*ranks, &r));
8484   PetscCall(DMDestroy(&rdm));
8485   PetscFunctionReturn(0);
8486 }
8487 
8488 /*@
8489   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8490 
8491   Input Parameters:
8492 + dm    - The DMPlex
8493 - label - The DMLabel
8494 
8495   Output Parameter:
8496 . val - The label value field
8497 
8498   Options Database Keys:
8499 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8500 
8501   Level: intermediate
8502 
8503 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMView()`
8504 @*/
8505 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8506 {
8507   DM           rdm;
8508   PetscFE      fe;
8509   PetscScalar *v;
8510   PetscInt     dim, cStart, cEnd, c;
8511 
8512   PetscFunctionBeginUser;
8513   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8514   PetscValidPointer(label, 2);
8515   PetscValidPointer(val, 3);
8516   PetscCall(DMClone(dm, &rdm));
8517   PetscCall(DMGetDimension(rdm, &dim));
8518   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8519   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8520   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8521   PetscCall(PetscFEDestroy(&fe));
8522   PetscCall(DMCreateDS(rdm));
8523   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8524   PetscCall(DMCreateGlobalVector(rdm, val));
8525   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8526   PetscCall(VecGetArray(*val, &v));
8527   for (c = cStart; c < cEnd; ++c) {
8528     PetscScalar *lv;
8529     PetscInt     cval;
8530 
8531     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8532     PetscCall(DMLabelGetValue(label, c, &cval));
8533     *lv = cval;
8534   }
8535   PetscCall(VecRestoreArray(*val, &v));
8536   PetscCall(DMDestroy(&rdm));
8537   PetscFunctionReturn(0);
8538 }
8539 
8540 /*@
8541   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8542 
8543   Input Parameter:
8544 . dm - The `DMPLEX` object
8545 
8546   Level: developer
8547 
8548   Notes:
8549   This is a useful diagnostic when creating meshes programmatically.
8550 
8551   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8552 
8553 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8554 @*/
8555 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8556 {
8557   PetscSection    coneSection, supportSection;
8558   const PetscInt *cone, *support;
8559   PetscInt        coneSize, c, supportSize, s;
8560   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8561   PetscBool       storagecheck = PETSC_TRUE;
8562 
8563   PetscFunctionBegin;
8564   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8565   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8566   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8567   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8568   /* Check that point p is found in the support of its cone points, and vice versa */
8569   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8570   for (p = pStart; p < pEnd; ++p) {
8571     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8572     PetscCall(DMPlexGetCone(dm, p, &cone));
8573     for (c = 0; c < coneSize; ++c) {
8574       PetscBool dup = PETSC_FALSE;
8575       PetscInt  d;
8576       for (d = c - 1; d >= 0; --d) {
8577         if (cone[c] == cone[d]) {
8578           dup = PETSC_TRUE;
8579           break;
8580         }
8581       }
8582       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8583       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8584       for (s = 0; s < supportSize; ++s) {
8585         if (support[s] == p) break;
8586       }
8587       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8588         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8589         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8590         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8591         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8592         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8593         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8594         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]);
8595         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8596       }
8597     }
8598     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8599     if (p != pp) {
8600       storagecheck = PETSC_FALSE;
8601       continue;
8602     }
8603     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8604     PetscCall(DMPlexGetSupport(dm, p, &support));
8605     for (s = 0; s < supportSize; ++s) {
8606       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8607       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8608       for (c = 0; c < coneSize; ++c) {
8609         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8610         if (cone[c] != pp) {
8611           c = 0;
8612           break;
8613         }
8614         if (cone[c] == p) break;
8615       }
8616       if (c >= coneSize) {
8617         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8618         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8619         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8620         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8621         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8622         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8623         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8624       }
8625     }
8626   }
8627   if (storagecheck) {
8628     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8629     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8630     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8631   }
8632   PetscFunctionReturn(0);
8633 }
8634 
8635 /*
8636   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.
8637 */
8638 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8639 {
8640   DMPolytopeType  cct;
8641   PetscInt        ptpoints[4];
8642   const PetscInt *cone, *ccone, *ptcone;
8643   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8644 
8645   PetscFunctionBegin;
8646   *unsplit = 0;
8647   switch (ct) {
8648   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8649     ptpoints[npt++] = c;
8650     break;
8651   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8652     PetscCall(DMPlexGetCone(dm, c, &cone));
8653     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8654     for (cp = 0; cp < coneSize; ++cp) {
8655       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8656       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8657     }
8658     break;
8659   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8660   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8661     PetscCall(DMPlexGetCone(dm, c, &cone));
8662     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8663     for (cp = 0; cp < coneSize; ++cp) {
8664       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8665       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8666       for (ccp = 0; ccp < cconeSize; ++ccp) {
8667         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8668         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8669           PetscInt p;
8670           for (p = 0; p < npt; ++p)
8671             if (ptpoints[p] == ccone[ccp]) break;
8672           if (p == npt) ptpoints[npt++] = ccone[ccp];
8673         }
8674       }
8675     }
8676     break;
8677   default:
8678     break;
8679   }
8680   for (pt = 0; pt < npt; ++pt) {
8681     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8682     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8683   }
8684   PetscFunctionReturn(0);
8685 }
8686 
8687 /*@
8688   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8689 
8690   Input Parameters:
8691 + dm - The `DMPLEX` object
8692 - cellHeight - Normally 0
8693 
8694   Level: developer
8695 
8696   Notes:
8697   This is a useful diagnostic when creating meshes programmatically.
8698   Currently applicable only to homogeneous simplex or tensor meshes.
8699 
8700   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8701 
8702 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8703 @*/
8704 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8705 {
8706   DMPlexInterpolatedFlag interp;
8707   DMPolytopeType         ct;
8708   PetscInt               vStart, vEnd, cStart, cEnd, c;
8709 
8710   PetscFunctionBegin;
8711   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8712   PetscCall(DMPlexIsInterpolated(dm, &interp));
8713   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8714   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8715   for (c = cStart; c < cEnd; ++c) {
8716     PetscInt *closure = NULL;
8717     PetscInt  coneSize, closureSize, cl, Nv = 0;
8718 
8719     PetscCall(DMPlexGetCellType(dm, c, &ct));
8720     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8721     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8722     if (interp == DMPLEX_INTERPOLATED_FULL) {
8723       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8724       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));
8725     }
8726     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8727     for (cl = 0; cl < closureSize * 2; cl += 2) {
8728       const PetscInt p = closure[cl];
8729       if ((p >= vStart) && (p < vEnd)) ++Nv;
8730     }
8731     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8732     /* Special Case: Tensor faces with identified vertices */
8733     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8734       PetscInt unsplit;
8735 
8736       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8737       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8738     }
8739     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));
8740   }
8741   PetscFunctionReturn(0);
8742 }
8743 
8744 /*@
8745   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8746 
8747   Collective on dm
8748 
8749   Input Parameters:
8750 + dm - The `DMPLEX` object
8751 - cellHeight - Normally 0
8752 
8753   Level: developer
8754 
8755   Notes:
8756   This is a useful diagnostic when creating meshes programmatically.
8757   This routine is only relevant for meshes that are fully interpolated across all ranks.
8758   It will error out if a partially interpolated mesh is given on some rank.
8759   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8760 
8761   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8762 
8763 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8764 @*/
8765 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8766 {
8767   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8768   DMPlexInterpolatedFlag interpEnum;
8769 
8770   PetscFunctionBegin;
8771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8772   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8773   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8774   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8775     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8776     PetscFunctionReturn(0);
8777   }
8778 
8779   PetscCall(DMGetDimension(dm, &dim));
8780   PetscCall(DMPlexGetDepth(dm, &depth));
8781   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8782   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8783     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8784     for (c = cStart; c < cEnd; ++c) {
8785       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8786       const DMPolytopeType *faceTypes;
8787       DMPolytopeType        ct;
8788       PetscInt              numFaces, coneSize, f;
8789       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8790 
8791       PetscCall(DMPlexGetCellType(dm, c, &ct));
8792       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8793       if (unsplit) continue;
8794       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8795       PetscCall(DMPlexGetCone(dm, c, &cone));
8796       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8797       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8798       for (cl = 0; cl < closureSize * 2; cl += 2) {
8799         const PetscInt p = closure[cl];
8800         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8801       }
8802       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8803       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);
8804       for (f = 0; f < numFaces; ++f) {
8805         DMPolytopeType fct;
8806         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8807 
8808         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8809         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8810         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8811           const PetscInt p = fclosure[cl];
8812           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8813         }
8814         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]);
8815         for (v = 0; v < fnumCorners; ++v) {
8816           if (fclosure[v] != faces[fOff + v]) {
8817             PetscInt v1;
8818 
8819             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8820             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8821             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8822             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8823             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8824             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]);
8825           }
8826         }
8827         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8828         fOff += faceSizes[f];
8829       }
8830       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8831       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8832     }
8833   }
8834   PetscFunctionReturn(0);
8835 }
8836 
8837 /*@
8838   DMPlexCheckGeometry - Check the geometry of mesh cells
8839 
8840   Input Parameter:
8841 . dm - The `DMPLEX` object
8842 
8843   Level: developer
8844 
8845   Notes:
8846   This is a useful diagnostic when creating meshes programmatically.
8847 
8848   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8849 
8850 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8851 @*/
8852 PetscErrorCode DMPlexCheckGeometry(DM dm)
8853 {
8854   Vec       coordinates;
8855   PetscReal detJ, J[9], refVol = 1.0;
8856   PetscReal vol;
8857   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8858 
8859   PetscFunctionBegin;
8860   PetscCall(DMGetDimension(dm, &dim));
8861   PetscCall(DMGetCoordinateDim(dm, &dE));
8862   if (dim != dE) PetscFunctionReturn(0);
8863   PetscCall(DMPlexGetDepth(dm, &depth));
8864   for (d = 0; d < dim; ++d) refVol *= 2.0;
8865   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8866   /* Make sure local coordinates are created, because that step is collective */
8867   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8868   if (!coordinates) PetscFunctionReturn(0);
8869   for (c = cStart; c < cEnd; ++c) {
8870     DMPolytopeType ct;
8871     PetscInt       unsplit;
8872     PetscBool      ignoreZeroVol = PETSC_FALSE;
8873 
8874     PetscCall(DMPlexGetCellType(dm, c, &ct));
8875     switch (ct) {
8876     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8877     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8878     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8879       ignoreZeroVol = PETSC_TRUE;
8880       break;
8881     default:
8882       break;
8883     }
8884     switch (ct) {
8885     case DM_POLYTOPE_TRI_PRISM:
8886     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8887     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8888     case DM_POLYTOPE_PYRAMID:
8889       continue;
8890     default:
8891       break;
8892     }
8893     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8894     if (unsplit) continue;
8895     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8896     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);
8897     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8898     /* This should work with periodicity since DG coordinates should be used */
8899     if (depth > 1) {
8900       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8901       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);
8902       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8903     }
8904   }
8905   PetscFunctionReturn(0);
8906 }
8907 
8908 /*@
8909   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8910 
8911   Collective on dm
8912 
8913   Input Parameters:
8914 + dm - The `DMPLEX` object
8915 . pointSF - The `PetscSF`, or NULL for `PointSF` attached to `DM`
8916 - allowExtraRoots - Flag to allow extra points not present in the `DM`
8917 
8918   Level: developer
8919 
8920   Notes:
8921   This is mainly intended for debugging/testing purposes.
8922 
8923   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8924 
8925   Extra roots can come from priodic cuts, where additional points appear on the boundary
8926 
8927 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
8928 @*/
8929 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
8930 {
8931   PetscInt           l, nleaves, nroots, overlap;
8932   const PetscInt    *locals;
8933   const PetscSFNode *remotes;
8934   PetscBool          distributed;
8935   MPI_Comm           comm;
8936   PetscMPIInt        rank;
8937 
8938   PetscFunctionBegin;
8939   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8940   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8941   else pointSF = dm->sf;
8942   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8943   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8944   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8945   {
8946     PetscMPIInt mpiFlag;
8947 
8948     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8949     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8950   }
8951   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8952   PetscCall(DMPlexIsDistributed(dm, &distributed));
8953   if (!distributed) {
8954     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);
8955     PetscFunctionReturn(0);
8956   }
8957   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);
8958   PetscCall(DMPlexGetOverlap(dm, &overlap));
8959 
8960   /* Check SF graph is compatible with DMPlex chart */
8961   {
8962     PetscInt pStart, pEnd, maxLeaf;
8963 
8964     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8965     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8966     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
8967     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8968   }
8969 
8970   /* Check Point SF has no local points referenced */
8971   for (l = 0; l < nleaves; l++) {
8972     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);
8973   }
8974 
8975   /* Check there are no cells in interface */
8976   if (!overlap) {
8977     PetscInt cellHeight, cStart, cEnd;
8978 
8979     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8980     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8981     for (l = 0; l < nleaves; ++l) {
8982       const PetscInt point = locals ? locals[l] : l;
8983 
8984       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8985     }
8986   }
8987 
8988   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8989   {
8990     const PetscInt *rootdegree;
8991 
8992     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8993     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8994     for (l = 0; l < nleaves; ++l) {
8995       const PetscInt  point = locals ? locals[l] : l;
8996       const PetscInt *cone;
8997       PetscInt        coneSize, c, idx;
8998 
8999       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9000       PetscCall(DMPlexGetCone(dm, point, &cone));
9001       for (c = 0; c < coneSize; ++c) {
9002         if (!rootdegree[cone[c]]) {
9003           if (locals) {
9004             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9005           } else {
9006             idx = (cone[c] < nleaves) ? cone[c] : -1;
9007           }
9008           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9009         }
9010       }
9011     }
9012   }
9013   PetscFunctionReturn(0);
9014 }
9015 
9016 /*@
9017   DMPlexCheck - Perform various checks of Plex sanity
9018 
9019   Input Parameter:
9020 . dm - The `DMPLEX` object
9021 
9022   Level: developer
9023 
9024   Notes:
9025   This is a useful diagnostic when creating meshes programmatically.
9026 
9027   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
9028 
9029   Currently does not include DMPlexCheckCellShape().
9030 
9031 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, DMCreate(), DMSetFromOptions()
9032 @*/
9033 PetscErrorCode DMPlexCheck(DM dm)
9034 {
9035   PetscInt cellHeight;
9036 
9037   PetscFunctionBegin;
9038   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9039   PetscCall(DMPlexCheckSymmetry(dm));
9040   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9041   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9042   PetscCall(DMPlexCheckGeometry(dm));
9043   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9044   PetscCall(DMPlexCheckInterfaceCones(dm));
9045   PetscFunctionReturn(0);
9046 }
9047 
9048 typedef struct cell_stats {
9049   PetscReal min, max, sum, squaresum;
9050   PetscInt  count;
9051 } cell_stats_t;
9052 
9053 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9054 {
9055   PetscInt i, N = *len;
9056 
9057   for (i = 0; i < N; i++) {
9058     cell_stats_t *A = (cell_stats_t *)a;
9059     cell_stats_t *B = (cell_stats_t *)b;
9060 
9061     B->min = PetscMin(A->min, B->min);
9062     B->max = PetscMax(A->max, B->max);
9063     B->sum += A->sum;
9064     B->squaresum += A->squaresum;
9065     B->count += A->count;
9066   }
9067 }
9068 
9069 /*@
9070   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9071 
9072   Collective on dm
9073 
9074   Input Parameters:
9075 + dm        - The `DMPLEX` object
9076 . output    - If true, statistics will be displayed on stdout
9077 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9078 
9079   Level: developer
9080 
9081   Notes:
9082   This is mainly intended for debugging/testing purposes.
9083 
9084   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9085 
9086 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9087 @*/
9088 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9089 {
9090   DM           dmCoarse;
9091   cell_stats_t stats, globalStats;
9092   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9093   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9094   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9095   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9096   PetscMPIInt  rank, size;
9097 
9098   PetscFunctionBegin;
9099   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9100   stats.min = PETSC_MAX_REAL;
9101   stats.max = PETSC_MIN_REAL;
9102   stats.sum = stats.squaresum = 0.;
9103   stats.count                 = 0;
9104 
9105   PetscCallMPI(MPI_Comm_size(comm, &size));
9106   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9107   PetscCall(DMGetCoordinateDim(dm, &cdim));
9108   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9109   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9110   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9111   for (c = cStart; c < cEnd; c++) {
9112     PetscInt  i;
9113     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9114 
9115     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9116     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9117     for (i = 0; i < PetscSqr(cdim); ++i) {
9118       frobJ += J[i] * J[i];
9119       frobInvJ += invJ[i] * invJ[i];
9120     }
9121     cond2 = frobJ * frobInvJ;
9122     cond  = PetscSqrtReal(cond2);
9123 
9124     stats.min = PetscMin(stats.min, cond);
9125     stats.max = PetscMax(stats.max, cond);
9126     stats.sum += cond;
9127     stats.squaresum += cond2;
9128     stats.count++;
9129     if (output && cond > limit) {
9130       PetscSection coordSection;
9131       Vec          coordsLocal;
9132       PetscScalar *coords = NULL;
9133       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9134 
9135       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9136       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9137       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9138       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9139       for (i = 0; i < Nv / cdim; ++i) {
9140         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9141         for (d = 0; d < cdim; ++d) {
9142           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9143           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9144         }
9145         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9146       }
9147       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9148       for (cl = 0; cl < clSize * 2; cl += 2) {
9149         const PetscInt edge = closure[cl];
9150 
9151         if ((edge >= eStart) && (edge < eEnd)) {
9152           PetscReal len;
9153 
9154           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9155           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9156         }
9157       }
9158       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9159       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9160     }
9161   }
9162   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9163 
9164   if (size > 1) {
9165     PetscMPIInt  blockLengths[2] = {4, 1};
9166     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9167     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9168     MPI_Op       statReduce;
9169 
9170     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9171     PetscCallMPI(MPI_Type_commit(&statType));
9172     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9173     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9174     PetscCallMPI(MPI_Op_free(&statReduce));
9175     PetscCallMPI(MPI_Type_free(&statType));
9176   } else {
9177     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9178   }
9179   if (rank == 0) {
9180     count = globalStats.count;
9181     min   = globalStats.min;
9182     max   = globalStats.max;
9183     mean  = globalStats.sum / globalStats.count;
9184     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9185   }
9186 
9187   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));
9188   PetscCall(PetscFree2(J, invJ));
9189 
9190   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9191   if (dmCoarse) {
9192     PetscBool isplex;
9193 
9194     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9195     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9196   }
9197   PetscFunctionReturn(0);
9198 }
9199 
9200 /*@
9201   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9202   orthogonal quality below given tolerance.
9203 
9204   Collective on dm
9205 
9206   Input Parameters:
9207 + dm   - The `DMPLEX` object
9208 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9209 - atol - [0, 1] Absolute tolerance for tagging cells.
9210 
9211   Output Parameters:
9212 + OrthQual      - Vec containing orthogonal quality per cell
9213 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9214 
9215   Options Database Keys:
9216 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9217 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
9218 
9219   Level: intermediate
9220 
9221   Notes:
9222   Orthogonal quality is given by the following formula:
9223 
9224   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9225 
9226   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
9227   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9228   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9229   calculating the cosine of the angle between these vectors.
9230 
9231   Orthogonal quality ranges from 1 (best) to 0 (worst).
9232 
9233   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9234   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9235 
9236   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9237 
9238 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9239 @*/
9240 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9241 {
9242   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9243   PetscInt              *idx;
9244   PetscScalar           *oqVals;
9245   const PetscScalar     *cellGeomArr, *faceGeomArr;
9246   PetscReal             *ci, *fi, *Ai;
9247   MPI_Comm               comm;
9248   Vec                    cellgeom, facegeom;
9249   DM                     dmFace, dmCell;
9250   IS                     glob;
9251   ISLocalToGlobalMapping ltog;
9252   PetscViewer            vwr;
9253 
9254   PetscFunctionBegin;
9255   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9256   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9257   PetscValidPointer(OrthQual, 4);
9258   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9259   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9260   PetscCall(DMGetDimension(dm, &nc));
9261   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9262   {
9263     DMPlexInterpolatedFlag interpFlag;
9264 
9265     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9266     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9267       PetscMPIInt rank;
9268 
9269       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9270       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9271     }
9272   }
9273   if (OrthQualLabel) {
9274     PetscValidPointer(OrthQualLabel, 5);
9275     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9276     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9277   } else {
9278     *OrthQualLabel = NULL;
9279   }
9280   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9281   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9282   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9283   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9284   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9285   PetscCall(VecCreate(comm, OrthQual));
9286   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9287   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9288   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9289   PetscCall(VecSetUp(*OrthQual));
9290   PetscCall(ISDestroy(&glob));
9291   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9292   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9293   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9294   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9295   PetscCall(VecGetDM(cellgeom, &dmCell));
9296   PetscCall(VecGetDM(facegeom, &dmFace));
9297   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9298   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9299     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9300     PetscInt         cellarr[2], *adj = NULL;
9301     PetscScalar     *cArr, *fArr;
9302     PetscReal        minvalc = 1.0, minvalf = 1.0;
9303     PetscFVCellGeom *cg;
9304 
9305     idx[cellIter] = cell - cStart;
9306     cellarr[0]    = cell;
9307     /* Make indexing into cellGeom easier */
9308     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9309     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9310     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9311     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9312     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9313       PetscInt         i;
9314       const PetscInt   neigh  = adj[cellneigh];
9315       PetscReal        normci = 0, normfi = 0, normai = 0;
9316       PetscFVCellGeom *cgneigh;
9317       PetscFVFaceGeom *fg;
9318 
9319       /* Don't count ourselves in the neighbor list */
9320       if (neigh == cell) continue;
9321       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9322       cellarr[1] = neigh;
9323       {
9324         PetscInt        numcovpts;
9325         const PetscInt *covpts;
9326 
9327         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9328         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9329         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9330       }
9331 
9332       /* Compute c_i, f_i and their norms */
9333       for (i = 0; i < nc; i++) {
9334         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9335         fi[i] = fg->centroid[i] - cg->centroid[i];
9336         Ai[i] = fg->normal[i];
9337         normci += PetscPowReal(ci[i], 2);
9338         normfi += PetscPowReal(fi[i], 2);
9339         normai += PetscPowReal(Ai[i], 2);
9340       }
9341       normci = PetscSqrtReal(normci);
9342       normfi = PetscSqrtReal(normfi);
9343       normai = PetscSqrtReal(normai);
9344 
9345       /* Normalize and compute for each face-cell-normal pair */
9346       for (i = 0; i < nc; i++) {
9347         ci[i] = ci[i] / normci;
9348         fi[i] = fi[i] / normfi;
9349         Ai[i] = Ai[i] / normai;
9350         /* PetscAbs because I don't know if normals are guaranteed to point out */
9351         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9352         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9353       }
9354       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9355       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9356     }
9357     PetscCall(PetscFree(adj));
9358     PetscCall(PetscFree2(cArr, fArr));
9359     /* Defer to cell if they're equal */
9360     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9361     if (OrthQualLabel) {
9362       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9363     }
9364   }
9365   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9366   PetscCall(VecAssemblyBegin(*OrthQual));
9367   PetscCall(VecAssemblyEnd(*OrthQual));
9368   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9369   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9370   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9371   if (OrthQualLabel) {
9372     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9373   }
9374   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9375   PetscCall(PetscViewerDestroy(&vwr));
9376   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9377   PetscFunctionReturn(0);
9378 }
9379 
9380 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9381  * interpolator construction */
9382 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9383 {
9384   PetscSection section, newSection, gsection;
9385   PetscSF      sf;
9386   PetscBool    hasConstraints, ghasConstraints;
9387 
9388   PetscFunctionBegin;
9389   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9390   PetscValidPointer(odm, 2);
9391   PetscCall(DMGetLocalSection(dm, &section));
9392   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9393   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9394   if (!ghasConstraints) {
9395     PetscCall(PetscObjectReference((PetscObject)dm));
9396     *odm = dm;
9397     PetscFunctionReturn(0);
9398   }
9399   PetscCall(DMClone(dm, odm));
9400   PetscCall(DMCopyFields(dm, *odm));
9401   PetscCall(DMGetLocalSection(*odm, &newSection));
9402   PetscCall(DMGetPointSF(*odm, &sf));
9403   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9404   PetscCall(DMSetGlobalSection(*odm, gsection));
9405   PetscCall(PetscSectionDestroy(&gsection));
9406   PetscFunctionReturn(0);
9407 }
9408 
9409 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9410 {
9411   DM        dmco, dmfo;
9412   Mat       interpo;
9413   Vec       rscale;
9414   Vec       cglobalo, clocal;
9415   Vec       fglobal, fglobalo, flocal;
9416   PetscBool regular;
9417 
9418   PetscFunctionBegin;
9419   PetscCall(DMGetFullDM(dmc, &dmco));
9420   PetscCall(DMGetFullDM(dmf, &dmfo));
9421   PetscCall(DMSetCoarseDM(dmfo, dmco));
9422   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9423   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9424   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9425   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9426   PetscCall(DMCreateLocalVector(dmc, &clocal));
9427   PetscCall(VecSet(cglobalo, 0.));
9428   PetscCall(VecSet(clocal, 0.));
9429   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9430   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9431   PetscCall(DMCreateLocalVector(dmf, &flocal));
9432   PetscCall(VecSet(fglobal, 0.));
9433   PetscCall(VecSet(fglobalo, 0.));
9434   PetscCall(VecSet(flocal, 0.));
9435   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9436   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9437   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9438   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9439   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9440   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9441   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9442   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9443   *shift = fglobal;
9444   PetscCall(VecDestroy(&flocal));
9445   PetscCall(VecDestroy(&fglobalo));
9446   PetscCall(VecDestroy(&clocal));
9447   PetscCall(VecDestroy(&cglobalo));
9448   PetscCall(VecDestroy(&rscale));
9449   PetscCall(MatDestroy(&interpo));
9450   PetscCall(DMDestroy(&dmfo));
9451   PetscCall(DMDestroy(&dmco));
9452   PetscFunctionReturn(0);
9453 }
9454 
9455 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9456 {
9457   PetscObject shifto;
9458   Vec         shift;
9459 
9460   PetscFunctionBegin;
9461   if (!interp) {
9462     Vec rscale;
9463 
9464     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9465     PetscCall(VecDestroy(&rscale));
9466   } else {
9467     PetscCall(PetscObjectReference((PetscObject)interp));
9468   }
9469   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9470   if (!shifto) {
9471     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9472     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9473     shifto = (PetscObject)shift;
9474     PetscCall(VecDestroy(&shift));
9475   }
9476   shift = (Vec)shifto;
9477   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9478   PetscCall(VecAXPY(fineSol, 1.0, shift));
9479   PetscCall(MatDestroy(&interp));
9480   PetscFunctionReturn(0);
9481 }
9482 
9483 /* Pointwise interpolation
9484      Just code FEM for now
9485      u^f = I u^c
9486      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9487      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9488      I_{ij} = psi^f_i phi^c_j
9489 */
9490 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9491 {
9492   PetscSection gsc, gsf;
9493   PetscInt     m, n;
9494   void        *ctx;
9495   DM           cdm;
9496   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9497 
9498   PetscFunctionBegin;
9499   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9500   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9501   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9502   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9503 
9504   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9505   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9506   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9507   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9508   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9509 
9510   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9511   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9512   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9513   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9514   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9515   if (scaling) {
9516     /* Use naive scaling */
9517     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9518   }
9519   PetscFunctionReturn(0);
9520 }
9521 
9522 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9523 {
9524   VecScatter ctx;
9525 
9526   PetscFunctionBegin;
9527   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9528   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9529   PetscCall(VecScatterDestroy(&ctx));
9530   PetscFunctionReturn(0);
9531 }
9532 
9533 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[])
9534 {
9535   const PetscInt Nc = uOff[1] - uOff[0];
9536   PetscInt       c;
9537   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9538 }
9539 
9540 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9541 {
9542   DM           dmc;
9543   PetscDS      ds;
9544   Vec          ones, locmass;
9545   IS           cellIS;
9546   PetscFormKey key;
9547   PetscInt     depth;
9548 
9549   PetscFunctionBegin;
9550   PetscCall(DMClone(dm, &dmc));
9551   PetscCall(DMCopyDisc(dm, dmc));
9552   PetscCall(DMGetDS(dmc, &ds));
9553   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9554   PetscCall(DMCreateGlobalVector(dmc, mass));
9555   PetscCall(DMGetLocalVector(dmc, &ones));
9556   PetscCall(DMGetLocalVector(dmc, &locmass));
9557   PetscCall(DMPlexGetDepth(dmc, &depth));
9558   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9559   PetscCall(VecSet(locmass, 0.0));
9560   PetscCall(VecSet(ones, 1.0));
9561   key.label = NULL;
9562   key.value = 0;
9563   key.field = 0;
9564   key.part  = 0;
9565   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9566   PetscCall(ISDestroy(&cellIS));
9567   PetscCall(VecSet(*mass, 0.0));
9568   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9569   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9570   PetscCall(DMRestoreLocalVector(dmc, &ones));
9571   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9572   PetscCall(DMDestroy(&dmc));
9573   PetscFunctionReturn(0);
9574 }
9575 
9576 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9577 {
9578   PetscSection gsc, gsf;
9579   PetscInt     m, n;
9580   void        *ctx;
9581   DM           cdm;
9582   PetscBool    regular;
9583 
9584   PetscFunctionBegin;
9585   if (dmFine == dmCoarse) {
9586     DM            dmc;
9587     PetscDS       ds;
9588     PetscWeakForm wf;
9589     Vec           u;
9590     IS            cellIS;
9591     PetscFormKey  key;
9592     PetscInt      depth;
9593 
9594     PetscCall(DMClone(dmFine, &dmc));
9595     PetscCall(DMCopyDisc(dmFine, dmc));
9596     PetscCall(DMGetDS(dmc, &ds));
9597     PetscCall(PetscDSGetWeakForm(ds, &wf));
9598     PetscCall(PetscWeakFormClear(wf));
9599     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9600     PetscCall(DMCreateMatrix(dmc, mass));
9601     PetscCall(DMGetLocalVector(dmc, &u));
9602     PetscCall(DMPlexGetDepth(dmc, &depth));
9603     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9604     PetscCall(MatZeroEntries(*mass));
9605     key.label = NULL;
9606     key.value = 0;
9607     key.field = 0;
9608     key.part  = 0;
9609     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9610     PetscCall(ISDestroy(&cellIS));
9611     PetscCall(DMRestoreLocalVector(dmc, &u));
9612     PetscCall(DMDestroy(&dmc));
9613   } else {
9614     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9615     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9616     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9617     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9618 
9619     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9620     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9621     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9622     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9623 
9624     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9625     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9626     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9627     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9628   }
9629   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9630   PetscFunctionReturn(0);
9631 }
9632 
9633 /*@
9634   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9635 
9636   Input Parameter:
9637 . dm - The `DMPLEX` object
9638 
9639   Output Parameter:
9640 . regular - The flag
9641 
9642   Level: intermediate
9643 
9644 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9645 @*/
9646 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9647 {
9648   PetscFunctionBegin;
9649   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9650   PetscValidBoolPointer(regular, 2);
9651   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9652   PetscFunctionReturn(0);
9653 }
9654 
9655 /*@
9656   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9657 
9658   Input Parameters:
9659 + dm - The `DMPLEX` object
9660 - regular - The flag
9661 
9662   Level: intermediate
9663 
9664 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9665 @*/
9666 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9667 {
9668   PetscFunctionBegin;
9669   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9670   ((DM_Plex *)dm->data)->regularRefinement = regular;
9671   PetscFunctionReturn(0);
9672 }
9673 
9674 /*@
9675   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9676   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9677 
9678   Not Collective
9679 
9680   Input Parameter:
9681 . dm - The `DMPLEX` object
9682 
9683   Output Parameters:
9684 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9685 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9686 
9687   Level: intermediate
9688 
9689 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9690 @*/
9691 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9692 {
9693   DM_Plex *plex = (DM_Plex *)dm->data;
9694 
9695   PetscFunctionBegin;
9696   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9697   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9698   if (anchorSection) *anchorSection = plex->anchorSection;
9699   if (anchorIS) *anchorIS = plex->anchorIS;
9700   PetscFunctionReturn(0);
9701 }
9702 
9703 /*@
9704   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9705   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9706   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9707 
9708   Collective on dm
9709 
9710   Input Parameters:
9711 + dm - The `DMPLEX` object
9712 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9713                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9714 - anchorIS - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9715 
9716   Level: intermediate
9717 
9718   Notes:
9719   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9720   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9721 
9722   The reference counts of anchorSection and anchorIS are incremented.
9723 
9724 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9725 @*/
9726 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9727 {
9728   DM_Plex    *plex = (DM_Plex *)dm->data;
9729   PetscMPIInt result;
9730 
9731   PetscFunctionBegin;
9732   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9733   if (anchorSection) {
9734     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9735     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9736     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9737   }
9738   if (anchorIS) {
9739     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9740     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9741     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9742   }
9743 
9744   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9745   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9746   plex->anchorSection = anchorSection;
9747 
9748   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9749   PetscCall(ISDestroy(&plex->anchorIS));
9750   plex->anchorIS = anchorIS;
9751 
9752   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9753     PetscInt        size, a, pStart, pEnd;
9754     const PetscInt *anchors;
9755 
9756     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9757     PetscCall(ISGetLocalSize(anchorIS, &size));
9758     PetscCall(ISGetIndices(anchorIS, &anchors));
9759     for (a = 0; a < size; a++) {
9760       PetscInt p;
9761 
9762       p = anchors[a];
9763       if (p >= pStart && p < pEnd) {
9764         PetscInt dof;
9765 
9766         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9767         if (dof) {
9768           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9769           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9770         }
9771       }
9772     }
9773     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9774   }
9775   /* reset the generic constraints */
9776   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9777   PetscFunctionReturn(0);
9778 }
9779 
9780 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9781 {
9782   PetscSection anchorSection;
9783   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9784 
9785   PetscFunctionBegin;
9786   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9787   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9788   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9789   PetscCall(PetscSectionGetNumFields(section, &numFields));
9790   if (numFields) {
9791     PetscInt f;
9792     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9793 
9794     for (f = 0; f < numFields; f++) {
9795       PetscInt numComp;
9796 
9797       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9798       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9799     }
9800   }
9801   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9802   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9803   pStart = PetscMax(pStart, sStart);
9804   pEnd   = PetscMin(pEnd, sEnd);
9805   pEnd   = PetscMax(pStart, pEnd);
9806   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9807   for (p = pStart; p < pEnd; p++) {
9808     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9809     if (dof) {
9810       PetscCall(PetscSectionGetDof(section, p, &dof));
9811       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9812       for (f = 0; f < numFields; f++) {
9813         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9814         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9815       }
9816     }
9817   }
9818   PetscCall(PetscSectionSetUp(*cSec));
9819   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9820   PetscFunctionReturn(0);
9821 }
9822 
9823 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9824 {
9825   PetscSection    aSec;
9826   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9827   const PetscInt *anchors;
9828   PetscInt        numFields, f;
9829   IS              aIS;
9830   MatType         mtype;
9831   PetscBool       iscuda, iskokkos;
9832 
9833   PetscFunctionBegin;
9834   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9835   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9836   PetscCall(PetscSectionGetStorageSize(section, &n));
9837   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9838   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9839   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9840   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9841   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9842   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9843   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9844   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9845   else mtype = MATSEQAIJ;
9846   PetscCall(MatSetType(*cMat, mtype));
9847   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9848   PetscCall(ISGetIndices(aIS, &anchors));
9849   /* cSec will be a subset of aSec and section */
9850   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9851   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9852   PetscCall(PetscMalloc1(m + 1, &i));
9853   i[0] = 0;
9854   PetscCall(PetscSectionGetNumFields(section, &numFields));
9855   for (p = pStart; p < pEnd; p++) {
9856     PetscInt rDof, rOff, r;
9857 
9858     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9859     if (!rDof) continue;
9860     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9861     if (numFields) {
9862       for (f = 0; f < numFields; f++) {
9863         annz = 0;
9864         for (r = 0; r < rDof; r++) {
9865           a = anchors[rOff + r];
9866           if (a < sStart || a >= sEnd) continue;
9867           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9868           annz += aDof;
9869         }
9870         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9871         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9872         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9873       }
9874     } else {
9875       annz = 0;
9876       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9877       for (q = 0; q < dof; q++) {
9878         a = anchors[rOff + q];
9879         if (a < sStart || a >= sEnd) continue;
9880         PetscCall(PetscSectionGetDof(section, a, &aDof));
9881         annz += aDof;
9882       }
9883       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9884       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9885       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9886     }
9887   }
9888   nnz = i[m];
9889   PetscCall(PetscMalloc1(nnz, &j));
9890   offset = 0;
9891   for (p = pStart; p < pEnd; p++) {
9892     if (numFields) {
9893       for (f = 0; f < numFields; f++) {
9894         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9895         for (q = 0; q < dof; q++) {
9896           PetscInt rDof, rOff, r;
9897           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9898           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9899           for (r = 0; r < rDof; r++) {
9900             PetscInt s;
9901 
9902             a = anchors[rOff + r];
9903             if (a < sStart || a >= sEnd) continue;
9904             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9905             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9906             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9907           }
9908         }
9909       }
9910     } else {
9911       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9912       for (q = 0; q < dof; q++) {
9913         PetscInt rDof, rOff, r;
9914         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9915         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9916         for (r = 0; r < rDof; r++) {
9917           PetscInt s;
9918 
9919           a = anchors[rOff + r];
9920           if (a < sStart || a >= sEnd) continue;
9921           PetscCall(PetscSectionGetDof(section, a, &aDof));
9922           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9923           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9924         }
9925       }
9926     }
9927   }
9928   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9929   PetscCall(PetscFree(i));
9930   PetscCall(PetscFree(j));
9931   PetscCall(ISRestoreIndices(aIS, &anchors));
9932   PetscFunctionReturn(0);
9933 }
9934 
9935 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
9936 {
9937   DM_Plex     *plex = (DM_Plex *)dm->data;
9938   PetscSection anchorSection, section, cSec;
9939   Mat          cMat;
9940 
9941   PetscFunctionBegin;
9942   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9943   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9944   if (anchorSection) {
9945     PetscInt Nf;
9946 
9947     PetscCall(DMGetLocalSection(dm, &section));
9948     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9949     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9950     PetscCall(DMGetNumFields(dm, &Nf));
9951     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9952     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9953     PetscCall(PetscSectionDestroy(&cSec));
9954     PetscCall(MatDestroy(&cMat));
9955   }
9956   PetscFunctionReturn(0);
9957 }
9958 
9959 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
9960 {
9961   IS           subis;
9962   PetscSection section, subsection;
9963 
9964   PetscFunctionBegin;
9965   PetscCall(DMGetLocalSection(dm, &section));
9966   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9967   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9968   /* Create subdomain */
9969   PetscCall(DMPlexFilter(dm, label, value, subdm));
9970   /* Create submodel */
9971   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9972   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9973   PetscCall(DMSetLocalSection(*subdm, subsection));
9974   PetscCall(PetscSectionDestroy(&subsection));
9975   PetscCall(DMCopyDisc(dm, *subdm));
9976   /* Create map from submodel to global model */
9977   if (is) {
9978     PetscSection    sectionGlobal, subsectionGlobal;
9979     IS              spIS;
9980     const PetscInt *spmap;
9981     PetscInt       *subIndices;
9982     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9983     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9984 
9985     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9986     PetscCall(ISGetIndices(spIS, &spmap));
9987     PetscCall(PetscSectionGetNumFields(section, &Nf));
9988     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9989     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9990     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9991     for (p = pStart; p < pEnd; ++p) {
9992       PetscInt gdof, pSubSize = 0;
9993 
9994       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9995       if (gdof > 0) {
9996         for (f = 0; f < Nf; ++f) {
9997           PetscInt fdof, fcdof;
9998 
9999           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10000           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10001           pSubSize += fdof - fcdof;
10002         }
10003         subSize += pSubSize;
10004         if (pSubSize) {
10005           if (bs < 0) {
10006             bs = pSubSize;
10007           } else if (bs != pSubSize) {
10008             /* Layout does not admit a pointwise block size */
10009             bs = 1;
10010           }
10011         }
10012       }
10013     }
10014     /* Must have same blocksize on all procs (some might have no points) */
10015     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10016     bsLocal[1] = bs;
10017     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10018     if (bsMinMax[0] != bsMinMax[1]) {
10019       bs = 1;
10020     } else {
10021       bs = bsMinMax[0];
10022     }
10023     PetscCall(PetscMalloc1(subSize, &subIndices));
10024     for (p = pStart; p < pEnd; ++p) {
10025       PetscInt gdof, goff;
10026 
10027       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10028       if (gdof > 0) {
10029         const PetscInt point = spmap[p];
10030 
10031         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10032         for (f = 0; f < Nf; ++f) {
10033           PetscInt fdof, fcdof, fc, f2, poff = 0;
10034 
10035           /* Can get rid of this loop by storing field information in the global section */
10036           for (f2 = 0; f2 < f; ++f2) {
10037             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10038             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10039             poff += fdof - fcdof;
10040           }
10041           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10042           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10043           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10044         }
10045       }
10046     }
10047     PetscCall(ISRestoreIndices(spIS, &spmap));
10048     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10049     if (bs > 1) {
10050       /* We need to check that the block size does not come from non-contiguous fields */
10051       PetscInt i, j, set = 1;
10052       for (i = 0; i < subSize; i += bs) {
10053         for (j = 0; j < bs; ++j) {
10054           if (subIndices[i + j] != subIndices[i] + j) {
10055             set = 0;
10056             break;
10057           }
10058         }
10059       }
10060       if (set) PetscCall(ISSetBlockSize(*is, bs));
10061     }
10062     /* Attach nullspace */
10063     for (f = 0; f < Nf; ++f) {
10064       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10065       if ((*subdm)->nullspaceConstructors[f]) break;
10066     }
10067     if (f < Nf) {
10068       MatNullSpace nullSpace;
10069       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10070 
10071       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10072       PetscCall(MatNullSpaceDestroy(&nullSpace));
10073     }
10074   }
10075   PetscFunctionReturn(0);
10076 }
10077 
10078 /*@
10079   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10080 
10081   Input Parameters:
10082 + dm - The `DM`
10083 - dummy - unused argument
10084 
10085   Options Database Key:
10086 . -dm_plex_monitor_throughput - Activate the monitor
10087 
10088   Level: developer
10089 
10090 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10091 @*/
10092 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10093 {
10094 #if defined(PETSC_USE_LOG)
10095   PetscStageLog      stageLog;
10096   PetscLogEvent      event;
10097   PetscLogStage      stage;
10098   PetscEventPerfInfo eventInfo;
10099   PetscReal          cellRate, flopRate;
10100   PetscInt           cStart, cEnd, Nf, N;
10101   const char        *name;
10102 #endif
10103 
10104   PetscFunctionBegin;
10105   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10106 #if defined(PETSC_USE_LOG)
10107   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10108   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10109   PetscCall(DMGetNumFields(dm, &Nf));
10110   PetscCall(PetscLogGetStageLog(&stageLog));
10111   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
10112   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10113   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
10114   N        = (cEnd - cStart) * Nf * eventInfo.count;
10115   flopRate = eventInfo.flops / eventInfo.time;
10116   cellRate = N / eventInfo.time;
10117   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)));
10118 #else
10119   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
10120 #endif
10121   PetscFunctionReturn(0);
10122 }
10123