xref: /petsc/src/dm/impls/plex/plex.c (revision 4dfa11a44d5adf2389f1d3acbc8f3c1116dc6c3a)
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   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
28   If the mesh has no cells, this returns PETSC_FALSE.
29 
30   Level: intermediate
31 
32 .seealso `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
33 @*/
34 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex) {
35   DMPolytopeType ct;
36   PetscInt       cStart, cEnd;
37 
38   PetscFunctionBegin;
39   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
40   if (cEnd <= cStart) {
41     *simplex = PETSC_FALSE;
42     PetscFunctionReturn(0);
43   }
44   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
45   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
46   PetscFunctionReturn(0);
47 }
48 
49 /*@
50   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
51 
52   Input Parameters:
53 + dm     - The DMPlex object
54 - height - The cell height in the Plex, 0 is the default
55 
56   Output Parameters:
57 + cStart - The first "normal" cell
58 - cEnd   - The upper bound on "normal"" cells
59 
60   Note: This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
61 
62   Level: developer
63 
64 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
65 @*/
66 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd) {
67   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
68   PetscInt       cS, cE, c;
69 
70   PetscFunctionBegin;
71   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
72   for (c = cS; c < cE; ++c) {
73     DMPolytopeType cct;
74 
75     PetscCall(DMPlexGetCellType(dm, c, &cct));
76     if ((PetscInt)cct < 0) break;
77     switch (cct) {
78     case DM_POLYTOPE_POINT:
79     case DM_POLYTOPE_SEGMENT:
80     case DM_POLYTOPE_TRIANGLE:
81     case DM_POLYTOPE_QUADRILATERAL:
82     case DM_POLYTOPE_TETRAHEDRON:
83     case DM_POLYTOPE_HEXAHEDRON: ct = cct; break;
84     default: break;
85     }
86     if (ct != DM_POLYTOPE_UNKNOWN) break;
87   }
88   if (ct != DM_POLYTOPE_UNKNOWN) {
89     DMLabel ctLabel;
90 
91     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
92     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
93     // Reset label for fast lookup
94     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
95   }
96   if (cStart) *cStart = cS;
97   if (cEnd) *cEnd = cE;
98   PetscFunctionReturn(0);
99 }
100 
101 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft) {
102   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
103   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
104 
105   PetscFunctionBegin;
106   *ft = PETSC_VTK_INVALID;
107   PetscCall(DMGetCoordinateDim(dm, &cdim));
108   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
109   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
110   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
111   if (field >= 0) {
112     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
113     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
114   } else {
115     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
116     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
117   }
118   PetscCallMPI(MPI_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
119   if (globalvcdof[0]) {
120     *sStart = vStart;
121     *sEnd   = vEnd;
122     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
123     else *ft = PETSC_VTK_POINT_FIELD;
124   } else if (globalvcdof[1]) {
125     *sStart = cStart;
126     *sEnd   = cEnd;
127     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
128     else *ft = PETSC_VTK_CELL_FIELD;
129   } else {
130     if (field >= 0) {
131       const char *fieldname;
132 
133       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
134       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
135     } else {
136       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
137     }
138   }
139   PetscFunctionReturn(0);
140 }
141 
142 /*@
143   DMPlexVecView1D - Plot many 1D solutions on the same line graph
144 
145   Collective on dm
146 
147   Input Parameters:
148 + dm - The DMPlex
149 . n  - The number of vectors
150 . u  - The array of local vectors
151 - viewer - The Draw viewer
152 
153   Level: advanced
154 
155 .seealso: `VecViewFromOptions()`, `VecView()`
156 @*/
157 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer) {
158   PetscDS            ds;
159   PetscDraw          draw = NULL;
160   PetscDrawLG        lg;
161   Vec                coordinates;
162   const PetscScalar *coords, **sol;
163   PetscReal         *vals;
164   PetscInt          *Nc;
165   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
166   char             **names;
167 
168   PetscFunctionBegin;
169   PetscCall(DMGetDS(dm, &ds));
170   PetscCall(PetscDSGetNumFields(ds, &Nf));
171   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
172   PetscCall(PetscDSGetComponents(ds, &Nc));
173 
174   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
175   if (!draw) PetscFunctionReturn(0);
176   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
177 
178   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
179   for (i = 0, l = 0; i < n; ++i) {
180     const char *vname;
181 
182     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
183     for (f = 0; f < Nf; ++f) {
184       PetscObject disc;
185       const char *fname;
186       char        tmpname[PETSC_MAX_PATH_LEN];
187 
188       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
189       /* TODO Create names for components */
190       for (c = 0; c < Nc[f]; ++c, ++l) {
191         PetscCall(PetscObjectGetName(disc, &fname));
192         PetscCall(PetscStrcpy(tmpname, vname));
193         PetscCall(PetscStrlcat(tmpname, ":", PETSC_MAX_PATH_LEN));
194         PetscCall(PetscStrlcat(tmpname, fname, PETSC_MAX_PATH_LEN));
195         PetscCall(PetscStrallocpy(tmpname, &names[l]));
196       }
197     }
198   }
199   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
200   /* Just add P_1 support for now */
201   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
202   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
203   PetscCall(VecGetArrayRead(coordinates, &coords));
204   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
205   for (v = vStart; v < vEnd; ++v) {
206     PetscScalar *x, *svals;
207 
208     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
209     for (i = 0; i < n; ++i) {
210       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
211       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
212     }
213     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
214   }
215   PetscCall(VecRestoreArrayRead(coordinates, &coords));
216   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
217   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
218   PetscCall(PetscFree3(sol, names, vals));
219 
220   PetscCall(PetscDrawLGDraw(lg));
221   PetscCall(PetscDrawLGDestroy(&lg));
222   PetscFunctionReturn(0);
223 }
224 
225 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer) {
226   DM dm;
227 
228   PetscFunctionBegin;
229   PetscCall(VecGetDM(u, &dm));
230   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
231   PetscFunctionReturn(0);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer) {
235   DM                 dm;
236   PetscSection       s;
237   PetscDraw          draw, popup;
238   DM                 cdm;
239   PetscSection       coordSection;
240   Vec                coordinates;
241   const PetscScalar *coords, *array;
242   PetscReal          bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
243   PetscReal          vbound[2], time;
244   PetscBool          flg;
245   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
246   const char        *name;
247   char               title[PETSC_MAX_PATH_LEN];
248 
249   PetscFunctionBegin;
250   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
251   PetscCall(VecGetDM(v, &dm));
252   PetscCall(DMGetCoordinateDim(dm, &dim));
253   PetscCall(DMGetLocalSection(dm, &s));
254   PetscCall(PetscSectionGetNumFields(s, &Nf));
255   PetscCall(DMGetCoarsenLevel(dm, &level));
256   PetscCall(DMGetCoordinateDM(dm, &cdm));
257   PetscCall(DMGetLocalSection(cdm, &coordSection));
258   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
259   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
260   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
261 
262   PetscCall(PetscObjectGetName((PetscObject)v, &name));
263   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
264 
265   PetscCall(VecGetLocalSize(coordinates, &N));
266   PetscCall(VecGetArrayRead(coordinates, &coords));
267   for (c = 0; c < N; c += dim) {
268     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
269     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
270     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
271     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
272   }
273   PetscCall(VecRestoreArrayRead(coordinates, &coords));
274   PetscCall(PetscDrawClear(draw));
275 
276   /* Could implement something like DMDASelectFields() */
277   for (f = 0; f < Nf; ++f) {
278     DM          fdm = dm;
279     Vec         fv  = v;
280     IS          fis;
281     char        prefix[PETSC_MAX_PATH_LEN];
282     const char *fname;
283 
284     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
285     PetscCall(PetscSectionGetFieldName(s, f, &fname));
286 
287     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
288     else prefix[0] = '\0';
289     if (Nf > 1) {
290       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
291       PetscCall(VecGetSubVector(v, fis, &fv));
292       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
293       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
294     }
295     for (comp = 0; comp < Nc; ++comp, ++w) {
296       PetscInt nmax = 2;
297 
298       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
299       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
300       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
301       PetscCall(PetscDrawSetTitle(draw, title));
302 
303       /* TODO Get max and min only for this component */
304       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
305       if (!flg) {
306         PetscCall(VecMin(fv, NULL, &vbound[0]));
307         PetscCall(VecMax(fv, NULL, &vbound[1]));
308         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
309       }
310       PetscCall(PetscDrawGetPopup(draw, &popup));
311       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
312       PetscCall(PetscDrawSetCoordinates(draw, bound[0], bound[1], bound[2], bound[3]));
313 
314       PetscCall(VecGetArrayRead(fv, &array));
315       for (c = cStart; c < cEnd; ++c) {
316         PetscScalar *coords = NULL, *a   = NULL;
317         PetscInt     numCoords, color[4] = {-1, -1, -1, -1};
318 
319         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
320         if (a) {
321           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
322           color[1] = color[2] = color[3] = color[0];
323         } else {
324           PetscScalar *vals = NULL;
325           PetscInt     numVals, va;
326 
327           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
328           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);
329           switch (numVals / Nc) {
330           case 3: /* P1 Triangle */
331           case 4: /* P1 Quadrangle */
332             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
333             break;
334           case 6: /* P2 Triangle */
335           case 8: /* P2 Quadrangle */
336             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
337             break;
338           default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
339           }
340           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
341         }
342         PetscCall(DMPlexVecGetClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
343         switch (numCoords) {
344         case 6:
345         case 12: /* Localized triangle */
346           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]));
347           break;
348         case 8:
349         case 16: /* Localized quadrilateral */
350           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]));
351           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]));
352           break;
353         default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
354         }
355         PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
356       }
357       PetscCall(VecRestoreArrayRead(fv, &array));
358       PetscCall(PetscDrawFlush(draw));
359       PetscCall(PetscDrawPause(draw));
360       PetscCall(PetscDrawSave(draw));
361     }
362     if (Nf > 1) {
363       PetscCall(VecRestoreSubVector(v, fis, &fv));
364       PetscCall(ISDestroy(&fis));
365       PetscCall(DMDestroy(&fdm));
366     }
367   }
368   PetscFunctionReturn(0);
369 }
370 
371 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer) {
372   DM        dm;
373   PetscDraw draw;
374   PetscInt  dim;
375   PetscBool isnull;
376 
377   PetscFunctionBegin;
378   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
379   PetscCall(PetscDrawIsNull(draw, &isnull));
380   if (isnull) PetscFunctionReturn(0);
381 
382   PetscCall(VecGetDM(v, &dm));
383   PetscCall(DMGetCoordinateDim(dm, &dim));
384   switch (dim) {
385   case 1: PetscCall(VecView_Plex_Local_Draw_1D(v, viewer)); break;
386   case 2: PetscCall(VecView_Plex_Local_Draw_2D(v, viewer)); break;
387   default: SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
388   }
389   PetscFunctionReturn(0);
390 }
391 
392 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer) {
393   DM                      dm;
394   Vec                     locv;
395   const char             *name;
396   PetscSection            section;
397   PetscInt                pStart, pEnd;
398   PetscInt                numFields;
399   PetscViewerVTKFieldType ft;
400 
401   PetscFunctionBegin;
402   PetscCall(VecGetDM(v, &dm));
403   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
404   PetscCall(PetscObjectGetName((PetscObject)v, &name));
405   PetscCall(PetscObjectSetName((PetscObject)locv, name));
406   PetscCall(VecCopy(v, locv));
407   PetscCall(DMGetLocalSection(dm, &section));
408   PetscCall(PetscSectionGetNumFields(section, &numFields));
409   if (!numFields) {
410     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
411     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
412   } else {
413     PetscInt f;
414 
415     for (f = 0; f < numFields; f++) {
416       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
417       if (ft == PETSC_VTK_INVALID) continue;
418       PetscCall(PetscObjectReference((PetscObject)locv));
419       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
420     }
421     PetscCall(VecDestroy(&locv));
422   }
423   PetscFunctionReturn(0);
424 }
425 
426 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer) {
427   DM        dm;
428   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
429 
430   PetscFunctionBegin;
431   PetscCall(VecGetDM(v, &dm));
432   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
433   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
434   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
435   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
436   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
437   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
438   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
439     PetscInt    i, numFields;
440     PetscObject fe;
441     PetscBool   fem  = PETSC_FALSE;
442     Vec         locv = v;
443     const char *name;
444     PetscInt    step;
445     PetscReal   time;
446 
447     PetscCall(DMGetNumFields(dm, &numFields));
448     for (i = 0; i < numFields; i++) {
449       PetscCall(DMGetField(dm, i, NULL, &fe));
450       if (fe->classid == PETSCFE_CLASSID) {
451         fem = PETSC_TRUE;
452         break;
453       }
454     }
455     if (fem) {
456       PetscObject isZero;
457 
458       PetscCall(DMGetLocalVector(dm, &locv));
459       PetscCall(PetscObjectGetName((PetscObject)v, &name));
460       PetscCall(PetscObjectSetName((PetscObject)locv, name));
461       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
462       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
463       PetscCall(VecCopy(v, locv));
464       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
465       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
466     }
467     if (isvtk) {
468       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
469     } else if (ishdf5) {
470 #if defined(PETSC_HAVE_HDF5)
471       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
472 #else
473       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
474 #endif
475     } else if (isdraw) {
476       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
477     } else if (isglvis) {
478       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
479       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
480       PetscCall(VecView_GLVis(locv, viewer));
481     } else if (iscgns) {
482 #if defined(PETSC_HAVE_CGNS)
483       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
484 #else
485       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
486 #endif
487     }
488     if (fem) {
489       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
490       PetscCall(DMRestoreLocalVector(dm, &locv));
491     }
492   } else {
493     PetscBool isseq;
494 
495     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
496     if (isseq) PetscCall(VecView_Seq(v, viewer));
497     else PetscCall(VecView_MPI(v, viewer));
498   }
499   PetscFunctionReturn(0);
500 }
501 
502 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer) {
503   DM        dm;
504   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
505 
506   PetscFunctionBegin;
507   PetscCall(VecGetDM(v, &dm));
508   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
509   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
510   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
511   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
512   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
513   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
514   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
515   if (isvtk || isdraw || isglvis || iscgns) {
516     Vec         locv;
517     PetscObject isZero;
518     const char *name;
519 
520     PetscCall(DMGetLocalVector(dm, &locv));
521     PetscCall(PetscObjectGetName((PetscObject)v, &name));
522     PetscCall(PetscObjectSetName((PetscObject)locv, name));
523     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
524     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
525     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
526     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
527     PetscCall(VecView_Plex_Local(locv, viewer));
528     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
529     PetscCall(DMRestoreLocalVector(dm, &locv));
530   } else if (ishdf5) {
531 #if defined(PETSC_HAVE_HDF5)
532     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
533 #else
534     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
535 #endif
536   } else if (isexodusii) {
537 #if defined(PETSC_HAVE_EXODUSII)
538     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
539 #else
540     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
541 #endif
542   } else {
543     PetscBool isseq;
544 
545     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
546     if (isseq) PetscCall(VecView_Seq(v, viewer));
547     else PetscCall(VecView_MPI(v, viewer));
548   }
549   PetscFunctionReturn(0);
550 }
551 
552 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer) {
553   DM                dm;
554   MPI_Comm          comm;
555   PetscViewerFormat format;
556   Vec               v;
557   PetscBool         isvtk, ishdf5;
558 
559   PetscFunctionBegin;
560   PetscCall(VecGetDM(originalv, &dm));
561   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
562   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
563   PetscCall(PetscViewerGetFormat(viewer, &format));
564   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
565   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
566   if (format == PETSC_VIEWER_NATIVE) {
567     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
568     /* this need a better fix */
569     if (dm->useNatural) {
570       if (dm->sfNatural) {
571         const char *vecname;
572         PetscInt    n, nroots;
573 
574         PetscCall(VecGetLocalSize(originalv, &n));
575         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
576         if (n == nroots) {
577           PetscCall(DMGetGlobalVector(dm, &v));
578           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
579           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
580           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
581           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
582         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
583       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
584     } else v = originalv;
585   } else v = originalv;
586 
587   if (ishdf5) {
588 #if defined(PETSC_HAVE_HDF5)
589     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
590 #else
591     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
592 #endif
593   } else if (isvtk) {
594     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
595   } else {
596     PetscBool isseq;
597 
598     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
599     if (isseq) PetscCall(VecView_Seq(v, viewer));
600     else PetscCall(VecView_MPI(v, viewer));
601   }
602   if (v != originalv) PetscCall(DMRestoreGlobalVector(dm, &v));
603   PetscFunctionReturn(0);
604 }
605 
606 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer) {
607   DM        dm;
608   PetscBool ishdf5;
609 
610   PetscFunctionBegin;
611   PetscCall(VecGetDM(v, &dm));
612   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
613   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
614   if (ishdf5) {
615     DM          dmBC;
616     Vec         gv;
617     const char *name;
618 
619     PetscCall(DMGetOutputDM(dm, &dmBC));
620     PetscCall(DMGetGlobalVector(dmBC, &gv));
621     PetscCall(PetscObjectGetName((PetscObject)v, &name));
622     PetscCall(PetscObjectSetName((PetscObject)gv, name));
623     PetscCall(VecLoad_Default(gv, viewer));
624     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
625     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
626     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
627   } else PetscCall(VecLoad_Default(v, viewer));
628   PetscFunctionReturn(0);
629 }
630 
631 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer) {
632   DM        dm;
633   PetscBool ishdf5, isexodusii;
634 
635   PetscFunctionBegin;
636   PetscCall(VecGetDM(v, &dm));
637   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
640   if (ishdf5) {
641 #if defined(PETSC_HAVE_HDF5)
642     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
643 #else
644     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
645 #endif
646   } else if (isexodusii) {
647 #if defined(PETSC_HAVE_EXODUSII)
648     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
649 #else
650     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
651 #endif
652   } else PetscCall(VecLoad_Default(v, viewer));
653   PetscFunctionReturn(0);
654 }
655 
656 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer) {
657   DM                dm;
658   PetscViewerFormat format;
659   PetscBool         ishdf5;
660 
661   PetscFunctionBegin;
662   PetscCall(VecGetDM(originalv, &dm));
663   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
664   PetscCall(PetscViewerGetFormat(viewer, &format));
665   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
666   if (format == PETSC_VIEWER_NATIVE) {
667     if (dm->useNatural) {
668       if (dm->sfNatural) {
669         if (ishdf5) {
670 #if defined(PETSC_HAVE_HDF5)
671           Vec         v;
672           const char *vecname;
673 
674           PetscCall(DMGetGlobalVector(dm, &v));
675           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
676           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
677           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
678           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
679           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
680           PetscCall(DMRestoreGlobalVector(dm, &v));
681 #else
682           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
683 #endif
684         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
685       }
686     } else PetscCall(VecLoad_Default(originalv, viewer));
687   }
688   PetscFunctionReturn(0);
689 }
690 
691 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer) {
692   PetscSection       coordSection;
693   Vec                coordinates;
694   DMLabel            depthLabel, celltypeLabel;
695   const char        *name[4];
696   const PetscScalar *a;
697   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
698 
699   PetscFunctionBegin;
700   PetscCall(DMGetDimension(dm, &dim));
701   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
702   PetscCall(DMGetCoordinateSection(dm, &coordSection));
703   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
704   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
705   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
706   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
707   PetscCall(VecGetArrayRead(coordinates, &a));
708   name[0]       = "vertex";
709   name[1]       = "edge";
710   name[dim - 1] = "face";
711   name[dim]     = "cell";
712   for (c = cStart; c < cEnd; ++c) {
713     PetscInt *closure = NULL;
714     PetscInt  closureSize, cl, ct;
715 
716     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
717     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
718     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
719     PetscCall(PetscViewerASCIIPushTab(viewer));
720     for (cl = 0; cl < closureSize * 2; cl += 2) {
721       PetscInt point = closure[cl], depth, dof, off, d, p;
722 
723       if ((point < pStart) || (point >= pEnd)) continue;
724       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
725       if (!dof) continue;
726       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
727       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
728       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
729       for (p = 0; p < dof / dim; ++p) {
730         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
731         for (d = 0; d < dim; ++d) {
732           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
733           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
734         }
735         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
736       }
737       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
738     }
739     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
740     PetscCall(PetscViewerASCIIPopTab(viewer));
741   }
742   PetscCall(VecRestoreArrayRead(coordinates, &a));
743   PetscFunctionReturn(0);
744 }
745 
746 typedef enum {
747   CS_CARTESIAN,
748   CS_POLAR,
749   CS_CYLINDRICAL,
750   CS_SPHERICAL
751 } CoordSystem;
752 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
753 
754 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[]) {
755   PetscInt i;
756 
757   PetscFunctionBegin;
758   if (dim > 3) {
759     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
760   } else {
761     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
762 
763     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
764     switch (cs) {
765     case CS_CARTESIAN:
766       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
767       break;
768     case CS_POLAR:
769       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
770       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
771       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
772       break;
773     case CS_CYLINDRICAL:
774       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
775       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
776       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
777       trcoords[2] = coords[2];
778       break;
779     case CS_SPHERICAL:
780       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
781       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
782       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
783       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
784       break;
785     }
786     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
787   }
788   PetscFunctionReturn(0);
789 }
790 
791 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer) {
792   DM_Plex          *mesh = (DM_Plex *)dm->data;
793   DM                cdm, cdmCell;
794   PetscSection      coordSection, coordSectionCell;
795   Vec               coordinates, coordinatesCell;
796   PetscViewerFormat format;
797 
798   PetscFunctionBegin;
799   PetscCall(DMGetCoordinateDM(dm, &cdm));
800   PetscCall(DMGetCoordinateSection(dm, &coordSection));
801   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
802   PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
803   PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
804   PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
805   PetscCall(PetscViewerGetFormat(viewer, &format));
806   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
807     const char *name;
808     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
809     PetscInt    pStart, pEnd, p, numLabels, l;
810     PetscMPIInt rank, size;
811 
812     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
813     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
814     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
815     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
816     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
817     PetscCall(DMGetDimension(dm, &dim));
818     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
819     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
820     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
821     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
822     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
823     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
824     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
825     for (p = pStart; p < pEnd; ++p) {
826       PetscInt dof, off, s;
827 
828       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
829       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
830       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
831     }
832     PetscCall(PetscViewerFlush(viewer));
833     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
834     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
835     for (p = pStart; p < pEnd; ++p) {
836       PetscInt dof, off, c;
837 
838       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
839       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
840       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]));
841     }
842     PetscCall(PetscViewerFlush(viewer));
843     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
844     if (coordSection && coordinates) {
845       CoordSystem        cs = CS_CARTESIAN;
846       const PetscScalar *array, *arrayCell = NULL;
847       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
848       PetscMPIInt        rank;
849       const char        *name;
850 
851       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
852       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
853       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
854       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
855       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
856       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
857       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
858       pStart = PetscMin(pvStart, pcStart);
859       pEnd   = PetscMax(pvEnd, pcEnd);
860       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
861       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
862       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
863       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
864 
865       PetscCall(VecGetArrayRead(coordinates, &array));
866       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
867       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
868       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
869       for (p = pStart; p < pEnd; ++p) {
870         PetscInt dof, off;
871 
872         if (p >= pvStart && p < pvEnd) {
873           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
874           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
875           if (dof) {
876             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
877             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
878             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
879           }
880         }
881         if (cdmCell && p >= pcStart && p < pcEnd) {
882           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
883           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
884           if (dof) {
885             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
886             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
887             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
888           }
889         }
890       }
891       PetscCall(PetscViewerFlush(viewer));
892       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
893       PetscCall(VecRestoreArrayRead(coordinates, &array));
894       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
895     }
896     PetscCall(DMGetNumLabels(dm, &numLabels));
897     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
898     for (l = 0; l < numLabels; ++l) {
899       DMLabel     label;
900       PetscBool   isdepth;
901       const char *name;
902 
903       PetscCall(DMGetLabelName(dm, l, &name));
904       PetscCall(PetscStrcmp(name, "depth", &isdepth));
905       if (isdepth) continue;
906       PetscCall(DMGetLabel(dm, name, &label));
907       PetscCall(DMLabelView(label, viewer));
908     }
909     if (size > 1) {
910       PetscSF sf;
911 
912       PetscCall(DMGetPointSF(dm, &sf));
913       PetscCall(PetscSFView(sf, viewer));
914     }
915     PetscCall(PetscViewerFlush(viewer));
916   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
917     const char  *name, *color;
918     const char  *defcolors[3]  = {"gray", "orange", "green"};
919     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
920     char         lname[PETSC_MAX_PATH_LEN];
921     PetscReal    scale      = 2.0;
922     PetscReal    tikzscale  = 1.0;
923     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
924     double       tcoords[3];
925     PetscScalar *coords;
926     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
927     PetscMPIInt  rank, size;
928     char       **names, **colors, **lcolors;
929     PetscBool    flg, lflg;
930     PetscBT      wp = NULL;
931     PetscInt     pEnd, pStart;
932 
933     PetscCall(DMGetDimension(dm, &dim));
934     PetscCall(DMPlexGetDepth(dm, &depth));
935     PetscCall(DMGetNumLabels(dm, &numLabels));
936     numLabels  = PetscMax(numLabels, 10);
937     numColors  = 10;
938     numLColors = 10;
939     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
940     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
941     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
942     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
943     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
944     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
945     n = 4;
946     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
947     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
948     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
949     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
950     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
951     if (!useLabels) numLabels = 0;
952     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
953     if (!useColors) {
954       numColors = 3;
955       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
956     }
957     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
958     if (!useColors) {
959       numLColors = 4;
960       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
961     }
962     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
963     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
964     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
965     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
966     if (depth < dim) plotEdges = PETSC_FALSE;
967     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
968 
969     /* filter points with labelvalue != labeldefaultvalue */
970     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
971     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
972     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
973     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
974     if (lflg) {
975       DMLabel lbl;
976 
977       PetscCall(DMGetLabel(dm, lname, &lbl));
978       if (lbl) {
979         PetscInt val, defval;
980 
981         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
982         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
983         for (c = pStart; c < pEnd; c++) {
984           PetscInt *closure = NULL;
985           PetscInt  closureSize;
986 
987           PetscCall(DMLabelGetValue(lbl, c, &val));
988           if (val == defval) continue;
989 
990           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
991           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
992           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
993         }
994       }
995     }
996 
997     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
998     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
999     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1000     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1001 \\documentclass[tikz]{standalone}\n\n\
1002 \\usepackage{pgflibraryshapes}\n\
1003 \\usetikzlibrary{backgrounds}\n\
1004 \\usetikzlibrary{arrows}\n\
1005 \\begin{document}\n"));
1006     if (size > 1) {
1007       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1008       for (p = 0; p < size; ++p) {
1009         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1010         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1011       }
1012       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1013     }
1014     if (drawHasse) {
1015       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1016 
1017       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1018       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1019       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1020       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1021       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1022       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1023       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1024       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1025       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1026       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1027       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1028       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1029     }
1030     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1031 
1032     /* Plot vertices */
1033     PetscCall(VecGetArray(coordinates, &coords));
1034     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1035     for (v = vStart; v < vEnd; ++v) {
1036       PetscInt  off, dof, d;
1037       PetscBool isLabeled = PETSC_FALSE;
1038 
1039       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1040       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1041       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1042       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1043       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1044       for (d = 0; d < dof; ++d) {
1045         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1046         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1047       }
1048       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1049       if (dim == 3) {
1050         PetscReal tmp = tcoords[1];
1051         tcoords[1]    = tcoords[2];
1052         tcoords[2]    = -tmp;
1053       }
1054       for (d = 0; d < dof; ++d) {
1055         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1056         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1057       }
1058       if (drawHasse) color = colors[0 % numColors];
1059       else color = colors[rank % numColors];
1060       for (l = 0; l < numLabels; ++l) {
1061         PetscInt val;
1062         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1063         if (val >= 0) {
1064           color     = lcolors[l % numLColors];
1065           isLabeled = PETSC_TRUE;
1066           break;
1067         }
1068       }
1069       if (drawNumbers[0]) {
1070         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1071       } else if (drawColors[0]) {
1072         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1073       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1074     }
1075     PetscCall(VecRestoreArray(coordinates, &coords));
1076     PetscCall(PetscViewerFlush(viewer));
1077     /* Plot edges */
1078     if (plotEdges) {
1079       PetscCall(VecGetArray(coordinates, &coords));
1080       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1081       for (e = eStart; e < eEnd; ++e) {
1082         const PetscInt *cone;
1083         PetscInt        coneSize, offA, offB, dof, d;
1084 
1085         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1086         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1087         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1088         PetscCall(DMPlexGetCone(dm, e, &cone));
1089         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1090         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1091         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1092         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1093         for (d = 0; d < dof; ++d) {
1094           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1095           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1096         }
1097         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1098         if (dim == 3) {
1099           PetscReal tmp = tcoords[1];
1100           tcoords[1]    = tcoords[2];
1101           tcoords[2]    = -tmp;
1102         }
1103         for (d = 0; d < dof; ++d) {
1104           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1105           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1106         }
1107         if (drawHasse) color = colors[1 % numColors];
1108         else color = colors[rank % numColors];
1109         for (l = 0; l < numLabels; ++l) {
1110           PetscInt val;
1111           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1112           if (val >= 0) {
1113             color = lcolors[l % numLColors];
1114             break;
1115           }
1116         }
1117         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1118       }
1119       PetscCall(VecRestoreArray(coordinates, &coords));
1120       PetscCall(PetscViewerFlush(viewer));
1121       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1122     }
1123     /* Plot cells */
1124     if (dim == 3 || !drawNumbers[1]) {
1125       for (e = eStart; e < eEnd; ++e) {
1126         const PetscInt *cone;
1127 
1128         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1129         color = colors[rank % numColors];
1130         for (l = 0; l < numLabels; ++l) {
1131           PetscInt val;
1132           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1133           if (val >= 0) {
1134             color = lcolors[l % numLColors];
1135             break;
1136           }
1137         }
1138         PetscCall(DMPlexGetCone(dm, e, &cone));
1139         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1140       }
1141     } else {
1142       DMPolytopeType ct;
1143 
1144       /* Drawing a 2D polygon */
1145       for (c = cStart; c < cEnd; ++c) {
1146         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1147         PetscCall(DMPlexGetCellType(dm, c, &ct));
1148         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1149           const PetscInt *cone;
1150           PetscInt        coneSize, e;
1151 
1152           PetscCall(DMPlexGetCone(dm, c, &cone));
1153           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1154           for (e = 0; e < coneSize; ++e) {
1155             const PetscInt *econe;
1156 
1157             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1158             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));
1159           }
1160         } else {
1161           PetscInt *closure = NULL;
1162           PetscInt  closureSize, Nv = 0, v;
1163 
1164           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1165           for (p = 0; p < closureSize * 2; p += 2) {
1166             const PetscInt point = closure[p];
1167 
1168             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1169           }
1170           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1171           for (v = 0; v <= Nv; ++v) {
1172             const PetscInt vertex = closure[v % Nv];
1173 
1174             if (v > 0) {
1175               if (plotEdges) {
1176                 const PetscInt *edge;
1177                 PetscInt        endpoints[2], ne;
1178 
1179                 endpoints[0] = closure[v - 1];
1180                 endpoints[1] = vertex;
1181                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1182                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1183                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1184                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1185               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1186             }
1187             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1188           }
1189           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1190           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1191         }
1192       }
1193     }
1194     for (c = cStart; c < cEnd; ++c) {
1195       double             ccoords[3] = {0.0, 0.0, 0.0};
1196       PetscBool          isLabeled  = PETSC_FALSE;
1197       PetscScalar       *cellCoords = NULL;
1198       const PetscScalar *array;
1199       PetscInt           numCoords, cdim, d;
1200       PetscBool          isDG;
1201 
1202       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1203       PetscCall(DMGetCoordinateDim(dm, &cdim));
1204       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1205       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1206       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1207       for (p = 0; p < numCoords / cdim; ++p) {
1208         for (d = 0; d < cdim; ++d) {
1209           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1210           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1211         }
1212         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1213         if (cdim == 3) {
1214           PetscReal tmp = tcoords[1];
1215           tcoords[1]    = tcoords[2];
1216           tcoords[2]    = -tmp;
1217         }
1218         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1219       }
1220       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1221       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1222       for (d = 0; d < cdim; ++d) {
1223         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1224         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1225       }
1226       if (drawHasse) color = colors[depth % numColors];
1227       else color = colors[rank % numColors];
1228       for (l = 0; l < numLabels; ++l) {
1229         PetscInt val;
1230         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1231         if (val >= 0) {
1232           color     = lcolors[l % numLColors];
1233           isLabeled = PETSC_TRUE;
1234           break;
1235         }
1236       }
1237       if (drawNumbers[dim]) {
1238         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1239       } else if (drawColors[dim]) {
1240         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1241       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1242     }
1243     if (drawHasse) {
1244       color = colors[depth % numColors];
1245       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1246       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1247       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1248       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1249       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1250 
1251       color = colors[1 % numColors];
1252       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1253       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1255       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1256       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1257 
1258       color = colors[0 % numColors];
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1262       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1263       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1264 
1265       for (p = pStart; p < pEnd; ++p) {
1266         const PetscInt *cone;
1267         PetscInt        coneSize, cp;
1268 
1269         PetscCall(DMPlexGetCone(dm, p, &cone));
1270         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1271         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1272       }
1273     }
1274     PetscCall(PetscViewerFlush(viewer));
1275     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1276     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1277     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1278     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1279     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1280     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1281     PetscCall(PetscFree3(names, colors, lcolors));
1282     PetscCall(PetscBTDestroy(&wp));
1283   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1284     Vec                    cown, acown;
1285     VecScatter             sct;
1286     ISLocalToGlobalMapping g2l;
1287     IS                     gid, acis;
1288     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1289     MPI_Group              ggroup, ngroup;
1290     PetscScalar           *array, nid;
1291     const PetscInt        *idxs;
1292     PetscInt              *idxs2, *start, *adjacency, *work;
1293     PetscInt64             lm[3], gm[3];
1294     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1295     PetscMPIInt            d1, d2, rank;
1296 
1297     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1298     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1299 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1300     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1301 #endif
1302     if (ncomm != MPI_COMM_NULL) {
1303       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1304       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1305       d1 = 0;
1306       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1307       nid = d2;
1308       PetscCallMPI(MPI_Group_free(&ggroup));
1309       PetscCallMPI(MPI_Group_free(&ngroup));
1310       PetscCallMPI(MPI_Comm_free(&ncomm));
1311     } else nid = 0.0;
1312 
1313     /* Get connectivity */
1314     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1315     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1316 
1317     /* filter overlapped local cells */
1318     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1319     PetscCall(ISGetIndices(gid, &idxs));
1320     PetscCall(ISGetLocalSize(gid, &cum));
1321     PetscCall(PetscMalloc1(cum, &idxs2));
1322     for (c = cStart, cum = 0; c < cEnd; c++) {
1323       if (idxs[c - cStart] < 0) continue;
1324       idxs2[cum++] = idxs[c - cStart];
1325     }
1326     PetscCall(ISRestoreIndices(gid, &idxs));
1327     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1328     PetscCall(ISDestroy(&gid));
1329     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1330 
1331     /* support for node-aware cell locality */
1332     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1333     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1334     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1335     PetscCall(VecGetArray(cown, &array));
1336     for (c = 0; c < numVertices; c++) array[c] = nid;
1337     PetscCall(VecRestoreArray(cown, &array));
1338     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1339     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1340     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1341     PetscCall(ISDestroy(&acis));
1342     PetscCall(VecScatterDestroy(&sct));
1343     PetscCall(VecDestroy(&cown));
1344 
1345     /* compute edgeCut */
1346     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1347     PetscCall(PetscMalloc1(cum, &work));
1348     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1349     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1350     PetscCall(ISDestroy(&gid));
1351     PetscCall(VecGetArray(acown, &array));
1352     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1353       PetscInt totl;
1354 
1355       totl = start[c + 1] - start[c];
1356       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1357       for (i = 0; i < totl; i++) {
1358         if (work[i] < 0) {
1359           ect += 1;
1360           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1361         }
1362       }
1363     }
1364     PetscCall(PetscFree(work));
1365     PetscCall(VecRestoreArray(acown, &array));
1366     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1367     lm[1] = -numVertices;
1368     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1369     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1370     lm[0] = ect;                     /* edgeCut */
1371     lm[1] = ectn;                    /* node-aware edgeCut */
1372     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1373     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1374     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1375 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1376     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1377 #else
1378     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1379 #endif
1380     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1381     PetscCall(PetscFree(start));
1382     PetscCall(PetscFree(adjacency));
1383     PetscCall(VecDestroy(&acown));
1384   } else {
1385     const char    *name;
1386     PetscInt      *sizes, *hybsizes, *ghostsizes;
1387     PetscInt       locDepth, depth, cellHeight, dim, d;
1388     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1389     PetscInt       numLabels, l, maxSize = 17;
1390     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1391     MPI_Comm       comm;
1392     PetscMPIInt    size, rank;
1393 
1394     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1395     PetscCallMPI(MPI_Comm_size(comm, &size));
1396     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1397     PetscCall(DMGetDimension(dm, &dim));
1398     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1399     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1400     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1401     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1402     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1403     PetscCall(DMPlexGetDepth(dm, &locDepth));
1404     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1405     PetscCall(DMPlexGetGhostCellStratum(dm, &gcStart, &gcEnd));
1406     gcNum = gcEnd - gcStart;
1407     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1408     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1409     for (d = 0; d <= depth; d++) {
1410       PetscInt Nc[2] = {0, 0}, ict;
1411 
1412       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1413       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1414       ict = ct0;
1415       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1416       ct0 = (DMPolytopeType)ict;
1417       for (p = pStart; p < pEnd; ++p) {
1418         DMPolytopeType ct;
1419 
1420         PetscCall(DMPlexGetCellType(dm, p, &ct));
1421         if (ct == ct0) ++Nc[0];
1422         else ++Nc[1];
1423       }
1424       if (size < maxSize) {
1425         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1426         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1427         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1428         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1429         for (p = 0; p < size; ++p) {
1430           if (rank == 0) {
1431             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1432             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1433             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1434           }
1435         }
1436       } else {
1437         PetscInt locMinMax[2];
1438 
1439         locMinMax[0] = Nc[0] + Nc[1];
1440         locMinMax[1] = Nc[0] + Nc[1];
1441         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1442         locMinMax[0] = Nc[1];
1443         locMinMax[1] = Nc[1];
1444         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1445         if (d == depth) {
1446           locMinMax[0] = gcNum;
1447           locMinMax[1] = gcNum;
1448           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1449         }
1450         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1451         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1452         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1453         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1454       }
1455       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1456     }
1457     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1458     {
1459       const PetscReal *maxCell;
1460       const PetscReal *L;
1461       PetscBool        localized;
1462 
1463       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1464       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1465       if (L || localized) {
1466         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1467         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1468         if (L) {
1469           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1470           for (d = 0; d < dim; ++d) {
1471             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1472             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1473           }
1474           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1475         }
1476         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1477         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1478       }
1479     }
1480     PetscCall(DMGetNumLabels(dm, &numLabels));
1481     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1482     for (l = 0; l < numLabels; ++l) {
1483       DMLabel         label;
1484       const char     *name;
1485       IS              valueIS;
1486       const PetscInt *values;
1487       PetscInt        numValues, v;
1488 
1489       PetscCall(DMGetLabelName(dm, l, &name));
1490       PetscCall(DMGetLabel(dm, name, &label));
1491       PetscCall(DMLabelGetNumValues(label, &numValues));
1492       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1493       PetscCall(DMLabelGetValueIS(label, &valueIS));
1494       PetscCall(ISGetIndices(valueIS, &values));
1495       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1496       for (v = 0; v < numValues; ++v) {
1497         PetscInt size;
1498 
1499         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1500         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1501         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1502       }
1503       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1504       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1505       PetscCall(ISRestoreIndices(valueIS, &values));
1506       PetscCall(ISDestroy(&valueIS));
1507     }
1508     {
1509       char    **labelNames;
1510       PetscInt  Nl = numLabels;
1511       PetscBool flg;
1512 
1513       PetscCall(PetscMalloc1(Nl, &labelNames));
1514       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1515       for (l = 0; l < Nl; ++l) {
1516         DMLabel label;
1517 
1518         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1519         if (flg) {
1520           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1521           PetscCall(DMLabelView(label, viewer));
1522         }
1523         PetscCall(PetscFree(labelNames[l]));
1524       }
1525       PetscCall(PetscFree(labelNames));
1526     }
1527     /* If no fields are specified, people do not want to see adjacency */
1528     if (dm->Nf) {
1529       PetscInt f;
1530 
1531       for (f = 0; f < dm->Nf; ++f) {
1532         const char *name;
1533 
1534         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1535         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1536         PetscCall(PetscViewerASCIIPushTab(viewer));
1537         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1538         if (dm->fields[f].adjacency[0]) {
1539           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1540           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1541         } else {
1542           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1543           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1544         }
1545         PetscCall(PetscViewerASCIIPopTab(viewer));
1546       }
1547     }
1548     PetscCall(DMGetCoarseDM(dm, &cdm));
1549     if (cdm) {
1550       PetscCall(PetscViewerASCIIPushTab(viewer));
1551       PetscCall(DMPlexView_Ascii(cdm, viewer));
1552       PetscCall(PetscViewerASCIIPopTab(viewer));
1553     }
1554   }
1555   PetscFunctionReturn(0);
1556 }
1557 
1558 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[]) {
1559   DMPolytopeType ct;
1560   PetscMPIInt    rank;
1561   PetscInt       cdim;
1562 
1563   PetscFunctionBegin;
1564   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1565   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1566   PetscCall(DMGetCoordinateDim(dm, &cdim));
1567   switch (ct) {
1568   case DM_POLYTOPE_SEGMENT:
1569   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1570     switch (cdim) {
1571     case 1: {
1572       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1573       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1574 
1575       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1576       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1577       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1578     } break;
1579     case 2: {
1580       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1581       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1582       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1583 
1584       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1585       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));
1586       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));
1587     } break;
1588     default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1589     }
1590     break;
1591   case DM_POLYTOPE_TRIANGLE:
1592     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));
1593     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1594     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1595     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1596     break;
1597   case DM_POLYTOPE_QUADRILATERAL:
1598     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));
1599     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));
1600     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1601     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1602     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1603     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1604     break;
1605   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1606   }
1607   PetscFunctionReturn(0);
1608 }
1609 
1610 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[]) {
1611   DMPolytopeType ct;
1612   PetscReal      centroid[2] = {0., 0.};
1613   PetscMPIInt    rank;
1614   PetscInt       fillColor, v, e, d;
1615 
1616   PetscFunctionBegin;
1617   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1618   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1619   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1620   switch (ct) {
1621   case DM_POLYTOPE_TRIANGLE: {
1622     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1623 
1624     for (v = 0; v < 3; ++v) {
1625       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1626       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1627     }
1628     for (e = 0; e < 3; ++e) {
1629       refCoords[0] = refVertices[e * 2 + 0];
1630       refCoords[1] = refVertices[e * 2 + 1];
1631       for (d = 1; d <= edgeDiv; ++d) {
1632         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1633         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1634       }
1635       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1636       for (d = 0; d < edgeDiv; ++d) {
1637         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));
1638         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1639       }
1640     }
1641   } break;
1642   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1643   }
1644   PetscFunctionReturn(0);
1645 }
1646 
1647 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer) {
1648   PetscDraw          draw;
1649   DM                 cdm;
1650   PetscSection       coordSection;
1651   Vec                coordinates;
1652   const PetscScalar *coords;
1653   PetscReal          xyl[2], xyr[2], bound[4] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
1654   PetscReal         *refCoords, *edgeCoords;
1655   PetscBool          isnull, drawAffine = PETSC_TRUE;
1656   PetscInt           dim, vStart, vEnd, cStart, cEnd, c, N, edgeDiv = 4;
1657 
1658   PetscFunctionBegin;
1659   PetscCall(DMGetCoordinateDim(dm, &dim));
1660   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1661   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1662   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1663   PetscCall(DMGetCoordinateDM(dm, &cdm));
1664   PetscCall(DMGetLocalSection(cdm, &coordSection));
1665   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1666   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1667   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1668 
1669   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1670   PetscCall(PetscDrawIsNull(draw, &isnull));
1671   if (isnull) PetscFunctionReturn(0);
1672   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1673 
1674   PetscCall(VecGetLocalSize(coordinates, &N));
1675   PetscCall(VecGetArrayRead(coordinates, &coords));
1676   for (c = 0; c < N; c += dim) {
1677     bound[0] = PetscMin(bound[0], PetscRealPart(coords[c]));
1678     bound[2] = PetscMax(bound[2], PetscRealPart(coords[c]));
1679     bound[1] = PetscMin(bound[1], PetscRealPart(coords[c + 1]));
1680     bound[3] = PetscMax(bound[3], PetscRealPart(coords[c + 1]));
1681   }
1682   PetscCall(VecRestoreArrayRead(coordinates, &coords));
1683   PetscCall(MPIU_Allreduce(&bound[0], xyl, 2, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)dm)));
1684   PetscCall(MPIU_Allreduce(&bound[2], xyr, 2, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)dm)));
1685   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1686   PetscCall(PetscDrawClear(draw));
1687 
1688   for (c = cStart; c < cEnd; ++c) {
1689     PetscScalar *coords = NULL;
1690     PetscInt     numCoords;
1691 
1692     PetscCall(DMPlexVecGetClosureAtDepth_Internal(dm, coordSection, coordinates, c, 0, &numCoords, &coords));
1693     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1694     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1695     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordinates, c, &numCoords, &coords));
1696   }
1697   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1698   PetscCall(PetscDrawFlush(draw));
1699   PetscCall(PetscDrawPause(draw));
1700   PetscCall(PetscDrawSave(draw));
1701   PetscFunctionReturn(0);
1702 }
1703 
1704 #if defined(PETSC_HAVE_EXODUSII)
1705 #include <exodusII.h>
1706 #include <petscviewerexodusii.h>
1707 #endif
1708 
1709 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer) {
1710   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1711   char      name[PETSC_MAX_PATH_LEN];
1712 
1713   PetscFunctionBegin;
1714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1715   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1716   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1717   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1718   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1719   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1720   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1721   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1722   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1723   if (iascii) {
1724     PetscViewerFormat format;
1725     PetscCall(PetscViewerGetFormat(viewer, &format));
1726     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1727     else PetscCall(DMPlexView_Ascii(dm, viewer));
1728   } else if (ishdf5) {
1729 #if defined(PETSC_HAVE_HDF5)
1730     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1731 #else
1732     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1733 #endif
1734   } else if (isvtk) {
1735     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1736   } else if (isdraw) {
1737     PetscCall(DMPlexView_Draw(dm, viewer));
1738   } else if (isglvis) {
1739     PetscCall(DMPlexView_GLVis(dm, viewer));
1740 #if defined(PETSC_HAVE_EXODUSII)
1741   } else if (isexodus) {
1742     /*
1743       exodusII requires that all sets be part of exactly one cell set.
1744       If the dm does not have a "Cell Sets" label defined, we create one
1745       with ID 1, containig all cells.
1746       Note that if the Cell Sets label is defined but does not cover all cells,
1747       we may still have a problem. This should probably be checked here or in the viewer;
1748     */
1749     PetscInt numCS;
1750     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1751     if (!numCS) {
1752       PetscInt cStart, cEnd, c;
1753       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1754       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1755       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1756     }
1757     PetscCall(DMView_PlexExodusII(dm, viewer));
1758 #endif
1759 #if defined(PETSC_HAVE_CGNS)
1760   } else if (iscgns) {
1761     PetscCall(DMView_PlexCGNS(dm, viewer));
1762 #endif
1763   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1764   /* Optionally view the partition */
1765   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1766   if (flg) {
1767     Vec ranks;
1768     PetscCall(DMPlexCreateRankField(dm, &ranks));
1769     PetscCall(VecView(ranks, viewer));
1770     PetscCall(VecDestroy(&ranks));
1771   }
1772   /* Optionally view a label */
1773   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1774   if (flg) {
1775     DMLabel label;
1776     Vec     val;
1777 
1778     PetscCall(DMGetLabel(dm, name, &label));
1779     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1780     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1781     PetscCall(VecView(val, viewer));
1782     PetscCall(VecDestroy(&val));
1783   }
1784   PetscFunctionReturn(0);
1785 }
1786 
1787 /*@
1788   DMPlexTopologyView - Saves a DMPlex topology into a file
1789 
1790   Collective on DM
1791 
1792   Input Parameters:
1793 + dm                - The DM whose topology is to be saved
1794 - viewer            - The PetscViewer for saving
1795 
1796   Level: advanced
1797 
1798 .seealso: `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`
1799 @*/
1800 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer) {
1801   PetscBool ishdf5;
1802 
1803   PetscFunctionBegin;
1804   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1805   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1806   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1807   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1808   if (ishdf5) {
1809 #if defined(PETSC_HAVE_HDF5)
1810     PetscViewerFormat format;
1811     PetscCall(PetscViewerGetFormat(viewer, &format));
1812     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1813       IS globalPointNumbering;
1814 
1815       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1816       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1817       PetscCall(ISDestroy(&globalPointNumbering));
1818     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1819 #else
1820     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1821 #endif
1822   }
1823   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1824   PetscFunctionReturn(0);
1825 }
1826 
1827 /*@
1828   DMPlexCoordinatesView - Saves DMPlex coordinates into a file
1829 
1830   Collective on DM
1831 
1832   Input Parameters:
1833 + dm     - The DM whose coordinates are to be saved
1834 - viewer - The PetscViewer for saving
1835 
1836   Level: advanced
1837 
1838 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`
1839 @*/
1840 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer) {
1841   PetscBool ishdf5;
1842 
1843   PetscFunctionBegin;
1844   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1845   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1846   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1847   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1848   if (ishdf5) {
1849 #if defined(PETSC_HAVE_HDF5)
1850     PetscViewerFormat format;
1851     PetscCall(PetscViewerGetFormat(viewer, &format));
1852     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1853       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1854     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1855 #else
1856     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1857 #endif
1858   }
1859   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1860   PetscFunctionReturn(0);
1861 }
1862 
1863 /*@
1864   DMPlexLabelsView - Saves DMPlex labels into a file
1865 
1866   Collective on DM
1867 
1868   Input Parameters:
1869 + dm     - The DM whose labels are to be saved
1870 - viewer - The PetscViewer for saving
1871 
1872   Level: advanced
1873 
1874 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`
1875 @*/
1876 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer) {
1877   PetscBool ishdf5;
1878 
1879   PetscFunctionBegin;
1880   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1881   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1882   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1883   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1884   if (ishdf5) {
1885 #if defined(PETSC_HAVE_HDF5)
1886     IS                globalPointNumbering;
1887     PetscViewerFormat format;
1888 
1889     PetscCall(PetscViewerGetFormat(viewer, &format));
1890     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1891       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1892       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1893       PetscCall(ISDestroy(&globalPointNumbering));
1894     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1895 #else
1896     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1897 #endif
1898   }
1899   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1900   PetscFunctionReturn(0);
1901 }
1902 
1903 /*@
1904   DMPlexSectionView - Saves a section associated with a DMPlex
1905 
1906   Collective on DM
1907 
1908   Input Parameters:
1909 + dm         - The DM that contains the topology on which the section to be saved is defined
1910 . viewer     - The PetscViewer for saving
1911 - sectiondm  - The DM that contains the section to be saved
1912 
1913   Level: advanced
1914 
1915   Notes:
1916   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.
1917 
1918   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.
1919 
1920 .seealso: `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`
1921 @*/
1922 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm) {
1923   PetscBool ishdf5;
1924 
1925   PetscFunctionBegin;
1926   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1927   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1928   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1929   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1930   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1931   if (ishdf5) {
1932 #if defined(PETSC_HAVE_HDF5)
1933     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1934 #else
1935     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1936 #endif
1937   }
1938   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1939   PetscFunctionReturn(0);
1940 }
1941 
1942 /*@
1943   DMPlexGlobalVectorView - Saves a global vector
1944 
1945   Collective on DM
1946 
1947   Input Parameters:
1948 + dm        - The DM that represents the topology
1949 . viewer    - The PetscViewer to save data with
1950 . sectiondm - The DM that contains the global section on which vec is defined
1951 - vec       - The global vector to be saved
1952 
1953   Level: advanced
1954 
1955   Notes:
1956   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.
1957 
1958   Typical calling sequence
1959 $       DMCreate(PETSC_COMM_WORLD, &dm);
1960 $       DMSetType(dm, DMPLEX);
1961 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
1962 $       DMClone(dm, &sectiondm);
1963 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
1964 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
1965 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
1966 $       PetscSectionSetChart(section, pStart, pEnd);
1967 $       PetscSectionSetUp(section);
1968 $       DMSetLocalSection(sectiondm, section);
1969 $       PetscSectionDestroy(&section);
1970 $       DMGetGlobalVector(sectiondm, &vec);
1971 $       PetscObjectSetName((PetscObject)vec, "vec_name");
1972 $       DMPlexTopologyView(dm, viewer);
1973 $       DMPlexSectionView(dm, viewer, sectiondm);
1974 $       DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
1975 $       DMRestoreGlobalVector(sectiondm, &vec);
1976 $       DMDestroy(&sectiondm);
1977 $       DMDestroy(&dm);
1978 
1979 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
1980 @*/
1981 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec) {
1982   PetscBool ishdf5;
1983 
1984   PetscFunctionBegin;
1985   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1986   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1987   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1988   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
1989   /* Check consistency */
1990   {
1991     PetscSection section;
1992     PetscBool    includesConstraints;
1993     PetscInt     m, m1;
1994 
1995     PetscCall(VecGetLocalSize(vec, &m1));
1996     PetscCall(DMGetGlobalSection(sectiondm, &section));
1997     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
1998     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
1999     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2000     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2001   }
2002   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2003   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2004   if (ishdf5) {
2005 #if defined(PETSC_HAVE_HDF5)
2006     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2007 #else
2008     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2009 #endif
2010   }
2011   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2012   PetscFunctionReturn(0);
2013 }
2014 
2015 /*@
2016   DMPlexLocalVectorView - Saves a local vector
2017 
2018   Collective on DM
2019 
2020   Input Parameters:
2021 + dm        - The DM that represents the topology
2022 . viewer    - The PetscViewer to save data with
2023 . sectiondm - The DM that contains the local section on which vec is defined; may be the same as dm
2024 - vec       - The local vector to be saved
2025 
2026   Level: advanced
2027 
2028   Notes:
2029   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.
2030 
2031   Typical calling sequence
2032 $       DMCreate(PETSC_COMM_WORLD, &dm);
2033 $       DMSetType(dm, DMPLEX);
2034 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2035 $       DMClone(dm, &sectiondm);
2036 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2037 $       PetscSectionCreate(PETSC_COMM_WORLD, &section);
2038 $       DMPlexGetChart(sectiondm, &pStart, &pEnd);
2039 $       PetscSectionSetChart(section, pStart, pEnd);
2040 $       PetscSectionSetUp(section);
2041 $       DMSetLocalSection(sectiondm, section);
2042 $       DMGetLocalVector(sectiondm, &vec);
2043 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2044 $       DMPlexTopologyView(dm, viewer);
2045 $       DMPlexSectionView(dm, viewer, sectiondm);
2046 $       DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2047 $       DMRestoreLocalVector(sectiondm, &vec);
2048 $       DMDestroy(&sectiondm);
2049 $       DMDestroy(&dm);
2050 
2051 .seealso: `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2052 @*/
2053 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec) {
2054   PetscBool ishdf5;
2055 
2056   PetscFunctionBegin;
2057   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2058   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2059   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2060   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2061   /* Check consistency */
2062   {
2063     PetscSection section;
2064     PetscBool    includesConstraints;
2065     PetscInt     m, m1;
2066 
2067     PetscCall(VecGetLocalSize(vec, &m1));
2068     PetscCall(DMGetLocalSection(sectiondm, &section));
2069     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2070     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2071     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2072     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2073   }
2074   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2075   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2076   if (ishdf5) {
2077 #if defined(PETSC_HAVE_HDF5)
2078     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2079 #else
2080     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2081 #endif
2082   }
2083   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2084   PetscFunctionReturn(0);
2085 }
2086 
2087 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer) {
2088   PetscBool ishdf5;
2089 
2090   PetscFunctionBegin;
2091   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2092   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2093   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2094   if (ishdf5) {
2095 #if defined(PETSC_HAVE_HDF5)
2096     PetscViewerFormat format;
2097     PetscCall(PetscViewerGetFormat(viewer, &format));
2098     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2099       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2100     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2101       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2102     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2103     PetscFunctionReturn(0);
2104 #else
2105     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2106 #endif
2107   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2108 }
2109 
2110 /*@
2111   DMPlexTopologyLoad - Loads a topology into a DMPlex
2112 
2113   Collective on DM
2114 
2115   Input Parameters:
2116 + dm                - The DM into which the topology is loaded
2117 - viewer            - The PetscViewer for the saved topology
2118 
2119   Output Parameters:
2120 . 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
2121 
2122   Level: advanced
2123 
2124 .seealso: `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2125 @*/
2126 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF) {
2127   PetscBool ishdf5;
2128 
2129   PetscFunctionBegin;
2130   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2131   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2132   if (globalToLocalPointSF) PetscValidPointer(globalToLocalPointSF, 3);
2133   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2134   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2135   if (ishdf5) {
2136 #if defined(PETSC_HAVE_HDF5)
2137     PetscViewerFormat format;
2138     PetscCall(PetscViewerGetFormat(viewer, &format));
2139     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2140       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2141     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
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_TopologyLoad, viewer, 0, 0, 0));
2147   PetscFunctionReturn(0);
2148 }
2149 
2150 /*@
2151   DMPlexCoordinatesLoad - Loads coordinates into a DMPlex
2152 
2153   Collective on DM
2154 
2155   Input Parameters:
2156 + dm     - The DM into which the coordinates are loaded
2157 . viewer - The PetscViewer for the saved coordinates
2158 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2159 
2160   Level: advanced
2161 
2162 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2163 @*/
2164 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF) {
2165   PetscBool ishdf5;
2166 
2167   PetscFunctionBegin;
2168   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2169   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2170   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2171   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2172   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2173   if (ishdf5) {
2174 #if defined(PETSC_HAVE_HDF5)
2175     PetscViewerFormat format;
2176     PetscCall(PetscViewerGetFormat(viewer, &format));
2177     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2178       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2179     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2180 #else
2181     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2182 #endif
2183   }
2184   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2185   PetscFunctionReturn(0);
2186 }
2187 
2188 /*@
2189   DMPlexLabelsLoad - Loads labels into a DMPlex
2190 
2191   Collective on DM
2192 
2193   Input Parameters:
2194 + dm     - The DM into which the labels are loaded
2195 . viewer - The PetscViewer for the saved labels
2196 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2197 
2198   Level: advanced
2199 
2200   Notes:
2201   The PetscSF argument must not be NULL if the DM is distributed, otherwise an error occurs.
2202 
2203 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`
2204 @*/
2205 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF) {
2206   PetscBool ishdf5;
2207 
2208   PetscFunctionBegin;
2209   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2210   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2211   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2212   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2213   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2214   if (ishdf5) {
2215 #if defined(PETSC_HAVE_HDF5)
2216     PetscViewerFormat format;
2217 
2218     PetscCall(PetscViewerGetFormat(viewer, &format));
2219     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2220       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2221     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2222 #else
2223     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2224 #endif
2225   }
2226   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2227   PetscFunctionReturn(0);
2228 }
2229 
2230 /*@
2231   DMPlexSectionLoad - Loads section into a DMPlex
2232 
2233   Collective on DM
2234 
2235   Input Parameters:
2236 + dm          - The DM that represents the topology
2237 . viewer      - The PetscViewer that represents the on-disk section (sectionA)
2238 . sectiondm   - The DM into which the on-disk section (sectionA) is migrated
2239 - globalToLocalPointSF - The SF returned by DMPlexTopologyLoad() when loading dm from viewer
2240 
2241   Output Parameters
2242 + 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)
2243 - 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)
2244 
2245   Level: advanced
2246 
2247   Notes:
2248   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.
2249 
2250   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.
2251 
2252   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.
2253 
2254   Example using 2 processes:
2255 $  NX (number of points on dm): 4
2256 $  sectionA                   : the on-disk section
2257 $  vecA                       : a vector associated with sectionA
2258 $  sectionB                   : sectiondm's local section constructed in this function
2259 $  vecB (local)               : a vector associated with sectiondm's local section
2260 $  vecB (global)              : a vector associated with sectiondm's global section
2261 $
2262 $                                     rank 0    rank 1
2263 $  vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2264 $  sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2265 $  sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2266 $  sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2267 $  [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2268 $  sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2269 $  sectionB->atlasDof             :     1 0 1 | 1 3
2270 $  sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2271 $  vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2272 $  vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2273 $
2274 $  where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2275 
2276 .seealso: `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`
2277 @*/
2278 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF) {
2279   PetscBool ishdf5;
2280 
2281   PetscFunctionBegin;
2282   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2283   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2284   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2285   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2286   if (globalDofSF) PetscValidPointer(globalDofSF, 5);
2287   if (localDofSF) PetscValidPointer(localDofSF, 6);
2288   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2289   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2290   if (ishdf5) {
2291 #if defined(PETSC_HAVE_HDF5)
2292     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2293 #else
2294     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2295 #endif
2296   }
2297   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2298   PetscFunctionReturn(0);
2299 }
2300 
2301 /*@
2302   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2303 
2304   Collective on DM
2305 
2306   Input Parameters:
2307 + dm        - The DM that represents the topology
2308 . viewer    - The PetscViewer that represents the on-disk vector data
2309 . sectiondm - The DM that contains the global section on which vec is defined
2310 . sf        - The SF that migrates the on-disk vector data into vec
2311 - vec       - The global vector to set values of
2312 
2313   Level: advanced
2314 
2315   Notes:
2316   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.
2317 
2318   Typical calling sequence
2319 $       DMCreate(PETSC_COMM_WORLD, &dm);
2320 $       DMSetType(dm, DMPLEX);
2321 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2322 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2323 $       DMClone(dm, &sectiondm);
2324 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2325 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2326 $       DMGetGlobalVector(sectiondm, &vec);
2327 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2328 $       DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2329 $       DMRestoreGlobalVector(sectiondm, &vec);
2330 $       PetscSFDestroy(&gsf);
2331 $       PetscSFDestroy(&sfX);
2332 $       DMDestroy(&sectiondm);
2333 $       DMDestroy(&dm);
2334 
2335 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2336 @*/
2337 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec) {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2344   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2345   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2346   /* Check consistency */
2347   {
2348     PetscSection section;
2349     PetscBool    includesConstraints;
2350     PetscInt     m, m1;
2351 
2352     PetscCall(VecGetLocalSize(vec, &m1));
2353     PetscCall(DMGetGlobalSection(sectiondm, &section));
2354     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2355     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2356     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2357     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2358   }
2359   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2360   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2361   if (ishdf5) {
2362 #if defined(PETSC_HAVE_HDF5)
2363     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2364 #else
2365     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2366 #endif
2367   }
2368   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2369   PetscFunctionReturn(0);
2370 }
2371 
2372 /*@
2373   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2374 
2375   Collective on DM
2376 
2377   Input Parameters:
2378 + dm        - The DM that represents the topology
2379 . viewer    - The PetscViewer that represents the on-disk vector data
2380 . sectiondm - The DM that contains the local section on which vec is defined
2381 . sf        - The SF that migrates the on-disk vector data into vec
2382 - vec       - The local vector to set values of
2383 
2384   Level: advanced
2385 
2386   Notes:
2387   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.
2388 
2389   Typical calling sequence
2390 $       DMCreate(PETSC_COMM_WORLD, &dm);
2391 $       DMSetType(dm, DMPLEX);
2392 $       PetscObjectSetName((PetscObject)dm, "topologydm_name");
2393 $       DMPlexTopologyLoad(dm, viewer, &sfX);
2394 $       DMClone(dm, &sectiondm);
2395 $       PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2396 $       DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2397 $       DMGetLocalVector(sectiondm, &vec);
2398 $       PetscObjectSetName((PetscObject)vec, "vec_name");
2399 $       DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2400 $       DMRestoreLocalVector(sectiondm, &vec);
2401 $       PetscSFDestroy(&lsf);
2402 $       PetscSFDestroy(&sfX);
2403 $       DMDestroy(&sectiondm);
2404 $       DMDestroy(&dm);
2405 
2406 .seealso: `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`
2407 @*/
2408 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec) {
2409   PetscBool ishdf5;
2410 
2411   PetscFunctionBegin;
2412   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2413   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2414   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2415   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2416   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2417   /* Check consistency */
2418   {
2419     PetscSection section;
2420     PetscBool    includesConstraints;
2421     PetscInt     m, m1;
2422 
2423     PetscCall(VecGetLocalSize(vec, &m1));
2424     PetscCall(DMGetLocalSection(sectiondm, &section));
2425     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2426     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2427     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2428     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2429   }
2430   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2431   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2432   if (ishdf5) {
2433 #if defined(PETSC_HAVE_HDF5)
2434     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2435 #else
2436     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2437 #endif
2438   }
2439   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2440   PetscFunctionReturn(0);
2441 }
2442 
2443 PetscErrorCode DMDestroy_Plex(DM dm) {
2444   DM_Plex *mesh = (DM_Plex *)dm->data;
2445 
2446   PetscFunctionBegin;
2447   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2448   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2449   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2450   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2451   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2452   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2453   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2454   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2455   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2456   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2457   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2458   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2459   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2460   if (--mesh->refct > 0) PetscFunctionReturn(0);
2461   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2462   PetscCall(PetscFree(mesh->cones));
2463   PetscCall(PetscFree(mesh->coneOrientations));
2464   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2465   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2466   PetscCall(PetscFree(mesh->supports));
2467   PetscCall(PetscFree(mesh->facesTmp));
2468   PetscCall(PetscFree(mesh->tetgenOpts));
2469   PetscCall(PetscFree(mesh->triangleOpts));
2470   PetscCall(PetscFree(mesh->transformType));
2471   PetscCall(PetscFree(mesh->distributionName));
2472   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2473   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2474   PetscCall(ISDestroy(&mesh->subpointIS));
2475   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2476   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2477   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2478   PetscCall(ISDestroy(&mesh->anchorIS));
2479   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2480   PetscCall(PetscFree(mesh->parents));
2481   PetscCall(PetscFree(mesh->childIDs));
2482   PetscCall(PetscSectionDestroy(&mesh->childSection));
2483   PetscCall(PetscFree(mesh->children));
2484   PetscCall(DMDestroy(&mesh->referenceTree));
2485   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2486   PetscCall(PetscFree(mesh->neighbors));
2487   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2488   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2489   PetscCall(PetscFree(mesh));
2490   PetscFunctionReturn(0);
2491 }
2492 
2493 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J) {
2494   PetscSection           sectionGlobal;
2495   PetscInt               bs = -1, mbs;
2496   PetscInt               localSize, localStart = 0;
2497   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2498   MatType                mtype;
2499   ISLocalToGlobalMapping ltog;
2500 
2501   PetscFunctionBegin;
2502   PetscCall(MatInitializePackage());
2503   mtype = dm->mattype;
2504   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2505   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2506   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2507   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2508   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2509   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2510   PetscCall(MatSetType(*J, mtype));
2511   PetscCall(MatSetFromOptions(*J));
2512   PetscCall(MatGetBlockSize(*J, &mbs));
2513   if (mbs > 1) bs = mbs;
2514   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2515   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2516   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2517   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2518   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2519   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2520   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2521   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2522   if (!isShell) {
2523     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2524     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2525     PetscInt  pStart, pEnd, p, dof, cdof;
2526 
2527     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2528 
2529     PetscCall(PetscCalloc1(localSize, &pblocks));
2530     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2531     for (p = pStart; p < pEnd; ++p) {
2532       PetscInt bdof, offset;
2533 
2534       PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2535       PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2536       PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2537       for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2538       dof  = dof < 0 ? -(dof + 1) : dof;
2539       bdof = cdof && (dof - cdof) ? 1 : dof;
2540       if (dof) {
2541         if (bs < 0) {
2542           bs = bdof;
2543         } else if (bs != bdof) {
2544           bs = 1;
2545         }
2546       }
2547     }
2548     /* Must have same blocksize on all procs (some might have no points) */
2549     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2550     bsLocal[1] = bs;
2551     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2552     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2553     else bs = bsMinMax[0];
2554     bs = PetscMax(1, bs);
2555     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2556     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2557       PetscCall(MatSetBlockSize(*J, bs));
2558       PetscCall(MatSetUp(*J));
2559     } else {
2560       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2561       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2562       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2563     }
2564     { // Consolidate blocks
2565       PetscInt nblocks = 0;
2566       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2567         if (pblocks[i] == 0) continue;
2568         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2569         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]);
2570       }
2571       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2572     }
2573     PetscCall(PetscFree(pblocks));
2574   }
2575   PetscCall(MatSetDM(*J, dm));
2576   PetscFunctionReturn(0);
2577 }
2578 
2579 /*@
2580   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2581 
2582   Not collective
2583 
2584   Input Parameter:
2585 . mesh - The DMPlex
2586 
2587   Output Parameters:
2588 . subsection - The subdomain section
2589 
2590   Level: developer
2591 
2592 .seealso:
2593 @*/
2594 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection) {
2595   DM_Plex *mesh = (DM_Plex *)dm->data;
2596 
2597   PetscFunctionBegin;
2598   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2599   if (!mesh->subdomainSection) {
2600     PetscSection section;
2601     PetscSF      sf;
2602 
2603     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2604     PetscCall(DMGetLocalSection(dm, &section));
2605     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2606     PetscCall(PetscSFDestroy(&sf));
2607   }
2608   *subsection = mesh->subdomainSection;
2609   PetscFunctionReturn(0);
2610 }
2611 
2612 /*@
2613   DMPlexGetChart - Return the interval for all mesh points [pStart, pEnd)
2614 
2615   Not collective
2616 
2617   Input Parameter:
2618 . mesh - The DMPlex
2619 
2620   Output Parameters:
2621 + pStart - The first mesh point
2622 - pEnd   - The upper bound for mesh points
2623 
2624   Level: beginner
2625 
2626 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`
2627 @*/
2628 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd) {
2629   DM_Plex *mesh = (DM_Plex *)dm->data;
2630 
2631   PetscFunctionBegin;
2632   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2633   PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2634   PetscFunctionReturn(0);
2635 }
2636 
2637 /*@
2638   DMPlexSetChart - Set the interval for all mesh points [pStart, pEnd)
2639 
2640   Not collective
2641 
2642   Input Parameters:
2643 + mesh - The DMPlex
2644 . pStart - The first mesh point
2645 - pEnd   - The upper bound for mesh points
2646 
2647   Output Parameters:
2648 
2649   Level: beginner
2650 
2651 .seealso: `DMPlexCreate()`, `DMPlexGetChart()`
2652 @*/
2653 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd) {
2654   DM_Plex *mesh = (DM_Plex *)dm->data;
2655 
2656   PetscFunctionBegin;
2657   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2658   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2659   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2660   PetscFunctionReturn(0);
2661 }
2662 
2663 /*@
2664   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2665 
2666   Not collective
2667 
2668   Input Parameters:
2669 + mesh - The DMPlex
2670 - p - The point, which must lie in the chart set with DMPlexSetChart()
2671 
2672   Output Parameter:
2673 . size - The cone size for point p
2674 
2675   Level: beginner
2676 
2677 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2678 @*/
2679 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size) {
2680   DM_Plex *mesh = (DM_Plex *)dm->data;
2681 
2682   PetscFunctionBegin;
2683   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2684   PetscValidIntPointer(size, 3);
2685   PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2686   PetscFunctionReturn(0);
2687 }
2688 
2689 /*@
2690   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2691 
2692   Not collective
2693 
2694   Input Parameters:
2695 + mesh - The DMPlex
2696 . p - The point, which must lie in the chart set with DMPlexSetChart()
2697 - size - The cone size for point p
2698 
2699   Output Parameter:
2700 
2701   Note:
2702   This should be called after DMPlexSetChart().
2703 
2704   Level: beginner
2705 
2706 .seealso: `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2707 @*/
2708 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size) {
2709   DM_Plex *mesh = (DM_Plex *)dm->data;
2710 
2711   PetscFunctionBegin;
2712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2713   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2714   PetscFunctionReturn(0);
2715 }
2716 
2717 /*@
2718   DMPlexAddConeSize - Add the given number of in-edges to this point in the DAG
2719 
2720   Not collective
2721 
2722   Input Parameters:
2723 + mesh - The DMPlex
2724 . p - The point, which must lie in the chart set with DMPlexSetChart()
2725 - size - The additional cone size for point p
2726 
2727   Output Parameter:
2728 
2729   Note:
2730   This should be called after DMPlexSetChart().
2731 
2732   Level: beginner
2733 
2734 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2735 @*/
2736 PetscErrorCode DMPlexAddConeSize(DM dm, PetscInt p, PetscInt size) {
2737   DM_Plex *mesh = (DM_Plex *)dm->data;
2738   PetscFunctionBegin;
2739   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2740   PetscCall(PetscSectionAddDof(mesh->coneSection, p, size));
2741   PetscFunctionReturn(0);
2742 }
2743 
2744 /*@C
2745   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2746 
2747   Not collective
2748 
2749   Input Parameters:
2750 + dm - The DMPlex
2751 - p - The point, which must lie in the chart set with DMPlexSetChart()
2752 
2753   Output Parameter:
2754 . cone - An array of points which are on the in-edges for point p
2755 
2756   Level: beginner
2757 
2758   Fortran Notes:
2759   Since it returns an array, this routine is only available in Fortran 90, and you must
2760   include petsc.h90 in your code.
2761   You must also call DMPlexRestoreCone() after you finish using the returned array.
2762   DMPlexRestoreCone() is not needed/available in C.
2763 
2764 .seealso: `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`
2765 @*/
2766 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[]) {
2767   DM_Plex *mesh = (DM_Plex *)dm->data;
2768   PetscInt off;
2769 
2770   PetscFunctionBegin;
2771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2772   PetscValidPointer(cone, 3);
2773   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2774   *cone = &mesh->cones[off];
2775   PetscFunctionReturn(0);
2776 }
2777 
2778 /*@C
2779   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2780 
2781   Not collective
2782 
2783   Input Parameters:
2784 + dm - The DMPlex
2785 - p - The IS of points, which must lie in the chart set with DMPlexSetChart()
2786 
2787   Output Parameters:
2788 + pConesSection - PetscSection describing the layout of pCones
2789 - pCones - An array of points which are on the in-edges for the point set p
2790 
2791   Level: intermediate
2792 
2793 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`
2794 @*/
2795 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones) {
2796   PetscSection cs, newcs;
2797   PetscInt    *cones;
2798   PetscInt    *newarr = NULL;
2799   PetscInt     n;
2800 
2801   PetscFunctionBegin;
2802   PetscCall(DMPlexGetCones(dm, &cones));
2803   PetscCall(DMPlexGetConeSection(dm, &cs));
2804   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2805   if (pConesSection) *pConesSection = newcs;
2806   if (pCones) {
2807     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2808     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2809   }
2810   PetscFunctionReturn(0);
2811 }
2812 
2813 /*@
2814   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2815 
2816   Not collective
2817 
2818   Input Parameters:
2819 + dm - The DMPlex
2820 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2821 
2822   Output Parameter:
2823 . expandedPoints - An array of vertices recursively expanded from input points
2824 
2825   Level: advanced
2826 
2827   Notes:
2828   Like DMPlexGetConeRecursive but returns only the 0-depth IS (i.e. vertices only) and no sections.
2829   There is no corresponding Restore function, just call ISDestroy() on the returned IS to deallocate.
2830 
2831 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetDepth()`
2832 @*/
2833 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints) {
2834   IS      *expandedPointsAll;
2835   PetscInt depth;
2836 
2837   PetscFunctionBegin;
2838   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2839   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2840   PetscValidPointer(expandedPoints, 3);
2841   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2842   *expandedPoints = expandedPointsAll[0];
2843   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2844   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2845   PetscFunctionReturn(0);
2846 }
2847 
2848 /*@
2849   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).
2850 
2851   Not collective
2852 
2853   Input Parameters:
2854 + dm - The DMPlex
2855 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2856 
2857   Output Parameters:
2858 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2859 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2860 - sections - (optional) An array of sections which describe mappings from points to their cone points
2861 
2862   Level: advanced
2863 
2864   Notes:
2865   Like DMPlexGetConeTuple() but recursive.
2866 
2867   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.
2868   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2869 
2870   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:
2871   (1) DAG points in expandedPoints[d+1] with depth d+1 to their cone points in expandedPoints[d];
2872   (2) DAG points in expandedPoints[d+1] with depth in [0,d] to the same points in expandedPoints[d].
2873 
2874 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2875 @*/
2876 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[]) {
2877   const PetscInt *arr0 = NULL, *cone = NULL;
2878   PetscInt       *arr = NULL, *newarr = NULL;
2879   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2880   IS             *expandedPoints_;
2881   PetscSection   *sections_;
2882 
2883   PetscFunctionBegin;
2884   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2885   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2886   if (depth) PetscValidIntPointer(depth, 3);
2887   if (expandedPoints) PetscValidPointer(expandedPoints, 4);
2888   if (sections) PetscValidPointer(sections, 5);
2889   PetscCall(ISGetLocalSize(points, &n));
2890   PetscCall(ISGetIndices(points, &arr0));
2891   PetscCall(DMPlexGetDepth(dm, &depth_));
2892   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2893   PetscCall(PetscCalloc1(depth_, &sections_));
2894   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2895   for (d = depth_ - 1; d >= 0; d--) {
2896     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2897     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2898     for (i = 0; i < n; i++) {
2899       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2900       if (arr[i] >= start && arr[i] < end) {
2901         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2902         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2903       } else {
2904         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2905       }
2906     }
2907     PetscCall(PetscSectionSetUp(sections_[d]));
2908     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2909     PetscCall(PetscMalloc1(newn, &newarr));
2910     for (i = 0; i < n; i++) {
2911       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2912       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2913       if (cn > 1) {
2914         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2915         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
2916       } else {
2917         newarr[co] = arr[i];
2918       }
2919     }
2920     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
2921     arr = newarr;
2922     n   = newn;
2923   }
2924   PetscCall(ISRestoreIndices(points, &arr0));
2925   *depth = depth_;
2926   if (expandedPoints) *expandedPoints = expandedPoints_;
2927   else {
2928     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
2929     PetscCall(PetscFree(expandedPoints_));
2930   }
2931   if (sections) *sections = sections_;
2932   else {
2933     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
2934     PetscCall(PetscFree(sections_));
2935   }
2936   PetscFunctionReturn(0);
2937 }
2938 
2939 /*@
2940   DMPlexRestoreConeRecursive - Deallocates arrays created by DMPlexGetConeRecursive
2941 
2942   Not collective
2943 
2944   Input Parameters:
2945 + dm - The DMPlex
2946 - points - The IS of points, which must lie in the chart set with DMPlexSetChart()
2947 
2948   Output Parameters:
2949 + depth - (optional) Size of the output arrays, equal to DMPlex depth, returned by DMPlexGetDepth()
2950 . expandedPoints - (optional) An array of recursively expanded cones
2951 - sections - (optional) An array of sections which describe mappings from points to their cone points
2952 
2953   Level: advanced
2954 
2955   Notes:
2956   See DMPlexGetConeRecursive() for details.
2957 
2958 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`, `DMPlexGetDepth()`
2959 @*/
2960 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[]) {
2961   PetscInt d, depth_;
2962 
2963   PetscFunctionBegin;
2964   PetscCall(DMPlexGetDepth(dm, &depth_));
2965   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
2966   if (depth) *depth = 0;
2967   if (expandedPoints) {
2968     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
2969     PetscCall(PetscFree(*expandedPoints));
2970   }
2971   if (sections) {
2972     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
2973     PetscCall(PetscFree(*sections));
2974   }
2975   PetscFunctionReturn(0);
2976 }
2977 
2978 /*@
2979   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
2980 
2981   Not collective
2982 
2983   Input Parameters:
2984 + mesh - The DMPlex
2985 . p - The point, which must lie in the chart set with DMPlexSetChart()
2986 - cone - An array of points which are on the in-edges for point p
2987 
2988   Output Parameter:
2989 
2990   Note:
2991   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
2992 
2993   Level: beginner
2994 
2995 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
2996 @*/
2997 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[]) {
2998   DM_Plex *mesh = (DM_Plex *)dm->data;
2999   PetscInt pStart, pEnd;
3000   PetscInt dof, off, c;
3001 
3002   PetscFunctionBegin;
3003   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3004   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3005   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3006   if (dof) PetscValidIntPointer(cone, 3);
3007   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3008   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);
3009   for (c = 0; c < dof; ++c) {
3010     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);
3011     mesh->cones[off + c] = cone[c];
3012   }
3013   PetscFunctionReturn(0);
3014 }
3015 
3016 /*@C
3017   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3018 
3019   Not collective
3020 
3021   Input Parameters:
3022 + mesh - The DMPlex
3023 - p - The point, which must lie in the chart set with DMPlexSetChart()
3024 
3025   Output Parameter:
3026 . coneOrientation - An array of orientations which are on the in-edges for point p. An orientation is an
3027                     integer giving the prescription for cone traversal.
3028 
3029   Level: beginner
3030 
3031   Notes:
3032   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3033   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3034   of o, however it is not necessarily the inverse. To get the inverse, use DMPolytopeTypeComposeOrientationInv()
3035   with the identity.
3036 
3037   Fortran Notes:
3038   Since it returns an array, this routine is only available in Fortran 90, and you must
3039   include petsc.h90 in your code.
3040   You must also call DMPlexRestoreConeOrientation() after you finish using the returned array.
3041   DMPlexRestoreConeOrientation() is not needed/available in C.
3042 
3043 .seealso: `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3044 @*/
3045 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[]) {
3046   DM_Plex *mesh = (DM_Plex *)dm->data;
3047   PetscInt off;
3048 
3049   PetscFunctionBegin;
3050   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3051   if (PetscDefined(USE_DEBUG)) {
3052     PetscInt dof;
3053     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3054     if (dof) PetscValidPointer(coneOrientation, 3);
3055   }
3056   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3057 
3058   *coneOrientation = &mesh->coneOrientations[off];
3059   PetscFunctionReturn(0);
3060 }
3061 
3062 /*@
3063   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3064 
3065   Not collective
3066 
3067   Input Parameters:
3068 + mesh - The DMPlex
3069 . p - The point, which must lie in the chart set with DMPlexSetChart()
3070 - coneOrientation - An array of orientations
3071   Output Parameter:
3072 
3073   Notes:
3074   This should be called after all calls to DMPlexSetConeSize() and DMSetUp().
3075 
3076   The meaning of coneOrientation is detailed in DMPlexGetConeOrientation().
3077 
3078   Level: beginner
3079 
3080 .seealso: `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3081 @*/
3082 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[]) {
3083   DM_Plex *mesh = (DM_Plex *)dm->data;
3084   PetscInt pStart, pEnd;
3085   PetscInt dof, off, c;
3086 
3087   PetscFunctionBegin;
3088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3089   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3090   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3091   if (dof) PetscValidIntPointer(coneOrientation, 3);
3092   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3093   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);
3094   for (c = 0; c < dof; ++c) {
3095     PetscInt cdof, o = coneOrientation[c];
3096 
3097     PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3098     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);
3099     mesh->coneOrientations[off + c] = o;
3100   }
3101   PetscFunctionReturn(0);
3102 }
3103 
3104 /*@
3105   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3106 
3107   Not collective
3108 
3109   Input Parameters:
3110 + mesh - The DMPlex
3111 . p - The point, which must lie in the chart set with DMPlexSetChart()
3112 . conePos - The local index in the cone where the point should be put
3113 - conePoint - The mesh point to insert
3114 
3115   Level: beginner
3116 
3117 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3118 @*/
3119 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint) {
3120   DM_Plex *mesh = (DM_Plex *)dm->data;
3121   PetscInt pStart, pEnd;
3122   PetscInt dof, off;
3123 
3124   PetscFunctionBegin;
3125   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3126   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3127   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);
3128   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);
3129   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3130   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3131   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);
3132   mesh->cones[off + conePos] = conePoint;
3133   PetscFunctionReturn(0);
3134 }
3135 
3136 /*@
3137   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3138 
3139   Not collective
3140 
3141   Input Parameters:
3142 + mesh - The DMPlex
3143 . p - The point, which must lie in the chart set with DMPlexSetChart()
3144 . conePos - The local index in the cone where the point should be put
3145 - coneOrientation - The point orientation to insert
3146 
3147   Level: beginner
3148 
3149   Notes:
3150   The meaning of coneOrientation values is detailed in DMPlexGetConeOrientation().
3151 
3152 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3153 @*/
3154 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation) {
3155   DM_Plex *mesh = (DM_Plex *)dm->data;
3156   PetscInt pStart, pEnd;
3157   PetscInt dof, off;
3158 
3159   PetscFunctionBegin;
3160   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3161   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3162   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);
3163   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3164   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3165   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);
3166   mesh->coneOrientations[off + conePos] = coneOrientation;
3167   PetscFunctionReturn(0);
3168 }
3169 
3170 /*@
3171   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3172 
3173   Not collective
3174 
3175   Input Parameters:
3176 + mesh - The DMPlex
3177 - p - The point, which must lie in the chart set with DMPlexSetChart()
3178 
3179   Output Parameter:
3180 . size - The support size for point p
3181 
3182   Level: beginner
3183 
3184 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3185 @*/
3186 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size) {
3187   DM_Plex *mesh = (DM_Plex *)dm->data;
3188 
3189   PetscFunctionBegin;
3190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3191   PetscValidIntPointer(size, 3);
3192   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3193   PetscFunctionReturn(0);
3194 }
3195 
3196 /*@
3197   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3198 
3199   Not collective
3200 
3201   Input Parameters:
3202 + mesh - The DMPlex
3203 . p - The point, which must lie in the chart set with DMPlexSetChart()
3204 - size - The support size for point p
3205 
3206   Output Parameter:
3207 
3208   Note:
3209   This should be called after DMPlexSetChart().
3210 
3211   Level: beginner
3212 
3213 .seealso: `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3214 @*/
3215 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size) {
3216   DM_Plex *mesh = (DM_Plex *)dm->data;
3217 
3218   PetscFunctionBegin;
3219   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3220   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3221   PetscFunctionReturn(0);
3222 }
3223 
3224 /*@C
3225   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3226 
3227   Not collective
3228 
3229   Input Parameters:
3230 + mesh - The DMPlex
3231 - p - The point, which must lie in the chart set with DMPlexSetChart()
3232 
3233   Output Parameter:
3234 . support - An array of points which are on the out-edges for point p
3235 
3236   Level: beginner
3237 
3238   Fortran Notes:
3239   Since it returns an array, this routine is only available in Fortran 90, and you must
3240   include petsc.h90 in your code.
3241   You must also call DMPlexRestoreSupport() after you finish using the returned array.
3242   DMPlexRestoreSupport() is not needed/available in C.
3243 
3244 .seealso: `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3245 @*/
3246 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[]) {
3247   DM_Plex *mesh = (DM_Plex *)dm->data;
3248   PetscInt off;
3249 
3250   PetscFunctionBegin;
3251   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3252   PetscValidPointer(support, 3);
3253   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3254   *support = &mesh->supports[off];
3255   PetscFunctionReturn(0);
3256 }
3257 
3258 /*@
3259   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3260 
3261   Not collective
3262 
3263   Input Parameters:
3264 + mesh - The DMPlex
3265 . p - The point, which must lie in the chart set with DMPlexSetChart()
3266 - support - An array of points which are on the out-edges for point p
3267 
3268   Output Parameter:
3269 
3270   Note:
3271   This should be called after all calls to DMPlexSetSupportSize() and DMSetUp().
3272 
3273   Level: beginner
3274 
3275 .seealso: `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3276 @*/
3277 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[]) {
3278   DM_Plex *mesh = (DM_Plex *)dm->data;
3279   PetscInt pStart, pEnd;
3280   PetscInt dof, off, c;
3281 
3282   PetscFunctionBegin;
3283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3284   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3285   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3286   if (dof) PetscValidIntPointer(support, 3);
3287   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3288   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);
3289   for (c = 0; c < dof; ++c) {
3290     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);
3291     mesh->supports[off + c] = support[c];
3292   }
3293   PetscFunctionReturn(0);
3294 }
3295 
3296 /*@
3297   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3298 
3299   Not collective
3300 
3301   Input Parameters:
3302 + mesh - The DMPlex
3303 . p - The point, which must lie in the chart set with DMPlexSetChart()
3304 . supportPos - The local index in the cone where the point should be put
3305 - supportPoint - The mesh point to insert
3306 
3307   Level: beginner
3308 
3309 .seealso: `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3310 @*/
3311 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint) {
3312   DM_Plex *mesh = (DM_Plex *)dm->data;
3313   PetscInt pStart, pEnd;
3314   PetscInt dof, off;
3315 
3316   PetscFunctionBegin;
3317   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3318   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3319   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3320   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3321   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);
3322   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);
3323   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);
3324   mesh->supports[off + supportPos] = supportPoint;
3325   PetscFunctionReturn(0);
3326 }
3327 
3328 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3329 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o) {
3330   switch (ct) {
3331   case DM_POLYTOPE_SEGMENT:
3332     if (o == -1) return -2;
3333     break;
3334   case DM_POLYTOPE_TRIANGLE:
3335     if (o == -3) return -1;
3336     if (o == -2) return -3;
3337     if (o == -1) return -2;
3338     break;
3339   case DM_POLYTOPE_QUADRILATERAL:
3340     if (o == -4) return -2;
3341     if (o == -3) return -1;
3342     if (o == -2) return -4;
3343     if (o == -1) return -3;
3344     break;
3345   default: return o;
3346   }
3347   return o;
3348 }
3349 
3350 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3351 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o) {
3352   switch (ct) {
3353   case DM_POLYTOPE_SEGMENT:
3354     if ((o == -2) || (o == 1)) return -1;
3355     if (o == -1) return 0;
3356     break;
3357   case DM_POLYTOPE_TRIANGLE:
3358     if (o == -3) return -2;
3359     if (o == -2) return -1;
3360     if (o == -1) return -3;
3361     break;
3362   case DM_POLYTOPE_QUADRILATERAL:
3363     if (o == -4) return -2;
3364     if (o == -3) return -1;
3365     if (o == -2) return -4;
3366     if (o == -1) return -3;
3367     break;
3368   default: return o;
3369   }
3370   return o;
3371 }
3372 
3373 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3374 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm) {
3375   PetscInt pStart, pEnd, p;
3376 
3377   PetscFunctionBegin;
3378   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3379   for (p = pStart; p < pEnd; ++p) {
3380     const PetscInt *cone, *ornt;
3381     PetscInt        coneSize, c;
3382 
3383     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3384     PetscCall(DMPlexGetCone(dm, p, &cone));
3385     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3386     for (c = 0; c < coneSize; ++c) {
3387       DMPolytopeType ct;
3388       const PetscInt o = ornt[c];
3389 
3390       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3391       switch (ct) {
3392       case DM_POLYTOPE_SEGMENT:
3393         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3394         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3395         break;
3396       case DM_POLYTOPE_TRIANGLE:
3397         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3398         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3399         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3400         break;
3401       case DM_POLYTOPE_QUADRILATERAL:
3402         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3403         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3404         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3405         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3406         break;
3407       default: break;
3408       }
3409     }
3410   }
3411   PetscFunctionReturn(0);
3412 }
3413 
3414 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3415   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3416   PetscInt       *closure;
3417   const PetscInt *tmp = NULL, *tmpO = NULL;
3418   PetscInt        off = 0, tmpSize, t;
3419 
3420   PetscFunctionBeginHot;
3421   if (ornt) {
3422     PetscCall(DMPlexGetCellType(dm, p, &ct));
3423     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3424   }
3425   if (*points) {
3426     closure = *points;
3427   } else {
3428     PetscInt maxConeSize, maxSupportSize;
3429     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3430     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3431   }
3432   if (useCone) {
3433     PetscCall(DMPlexGetConeSize(dm, p, &tmpSize));
3434     PetscCall(DMPlexGetCone(dm, p, &tmp));
3435     PetscCall(DMPlexGetConeOrientation(dm, p, &tmpO));
3436   } else {
3437     PetscCall(DMPlexGetSupportSize(dm, p, &tmpSize));
3438     PetscCall(DMPlexGetSupport(dm, p, &tmp));
3439   }
3440   if (ct == DM_POLYTOPE_UNKNOWN) {
3441     closure[off++] = p;
3442     closure[off++] = 0;
3443     for (t = 0; t < tmpSize; ++t) {
3444       closure[off++] = tmp[t];
3445       closure[off++] = tmpO ? tmpO[t] : 0;
3446     }
3447   } else {
3448     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3449 
3450     /* We assume that cells with a valid type have faces with a valid type */
3451     closure[off++] = p;
3452     closure[off++] = ornt;
3453     for (t = 0; t < tmpSize; ++t) {
3454       DMPolytopeType ft;
3455 
3456       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3457       closure[off++] = tmp[arr[t]];
3458       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3459     }
3460   }
3461   if (numPoints) *numPoints = tmpSize + 1;
3462   if (points) *points = closure;
3463   PetscFunctionReturn(0);
3464 }
3465 
3466 /* We need a special tensor verison becasue we want to allow duplicate points in the endcaps for hybrid cells */
3467 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points) {
3468   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3469   const PetscInt *cone, *ornt;
3470   PetscInt       *pts, *closure = NULL;
3471   DMPolytopeType  ft;
3472   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3473   PetscInt        dim, coneSize, c, d, clSize, cl;
3474 
3475   PetscFunctionBeginHot;
3476   PetscCall(DMGetDimension(dm, &dim));
3477   PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3478   PetscCall(DMPlexGetCone(dm, point, &cone));
3479   PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3480   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3481   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3482   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3483   maxSize       = PetscMax(coneSeries, supportSeries);
3484   if (*points) {
3485     pts = *points;
3486   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3487   c        = 0;
3488   pts[c++] = point;
3489   pts[c++] = o;
3490   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3491   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3492   for (cl = 0; cl < clSize * 2; cl += 2) {
3493     pts[c++] = closure[cl];
3494     pts[c++] = closure[cl + 1];
3495   }
3496   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3497   for (cl = 0; cl < clSize * 2; cl += 2) {
3498     pts[c++] = closure[cl];
3499     pts[c++] = closure[cl + 1];
3500   }
3501   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3502   for (d = 2; d < coneSize; ++d) {
3503     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3504     pts[c++] = cone[arr[d * 2 + 0]];
3505     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3506   }
3507   if (dim >= 3) {
3508     for (d = 2; d < coneSize; ++d) {
3509       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3510       const PetscInt *fcone, *fornt;
3511       PetscInt        fconeSize, fc, i;
3512 
3513       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3514       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3515       PetscCall(DMPlexGetConeSize(dm, fpoint, &fconeSize));
3516       PetscCall(DMPlexGetCone(dm, fpoint, &fcone));
3517       PetscCall(DMPlexGetConeOrientation(dm, fpoint, &fornt));
3518       for (fc = 0; fc < fconeSize; ++fc) {
3519         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3520         const PetscInt co = farr[fc * 2 + 1];
3521 
3522         for (i = 0; i < c; i += 2)
3523           if (pts[i] == cp) break;
3524         if (i == c) {
3525           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3526           pts[c++] = cp;
3527           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3528         }
3529       }
3530     }
3531   }
3532   *numPoints = c / 2;
3533   *points    = pts;
3534   PetscFunctionReturn(0);
3535 }
3536 
3537 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3538   DMPolytopeType ct;
3539   PetscInt      *closure, *fifo;
3540   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3541   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3542   PetscInt       depth, maxSize;
3543 
3544   PetscFunctionBeginHot;
3545   PetscCall(DMPlexGetDepth(dm, &depth));
3546   if (depth == 1) {
3547     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3548     PetscFunctionReturn(0);
3549   }
3550   PetscCall(DMPlexGetCellType(dm, p, &ct));
3551   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3552   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3553     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3554     PetscFunctionReturn(0);
3555   }
3556   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3557   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3558   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3559   maxSize       = PetscMax(coneSeries, supportSeries);
3560   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3561   if (*points) {
3562     closure = *points;
3563   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3564   closure[closureSize++] = p;
3565   closure[closureSize++] = ornt;
3566   fifo[fifoSize++]       = p;
3567   fifo[fifoSize++]       = ornt;
3568   fifo[fifoSize++]       = ct;
3569   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3570   while (fifoSize - fifoStart) {
3571     const PetscInt       q    = fifo[fifoStart++];
3572     const PetscInt       o    = fifo[fifoStart++];
3573     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3574     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3575     const PetscInt      *tmp, *tmpO;
3576     PetscInt             tmpSize, t;
3577 
3578     if (PetscDefined(USE_DEBUG)) {
3579       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3580       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);
3581     }
3582     if (useCone) {
3583       PetscCall(DMPlexGetConeSize(dm, q, &tmpSize));
3584       PetscCall(DMPlexGetCone(dm, q, &tmp));
3585       PetscCall(DMPlexGetConeOrientation(dm, q, &tmpO));
3586     } else {
3587       PetscCall(DMPlexGetSupportSize(dm, q, &tmpSize));
3588       PetscCall(DMPlexGetSupport(dm, q, &tmp));
3589       tmpO = NULL;
3590     }
3591     for (t = 0; t < tmpSize; ++t) {
3592       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3593       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3594       const PetscInt cp = tmp[ip];
3595       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3596       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3597       PetscInt       c;
3598 
3599       /* Check for duplicate */
3600       for (c = 0; c < closureSize; c += 2) {
3601         if (closure[c] == cp) break;
3602       }
3603       if (c == closureSize) {
3604         closure[closureSize++] = cp;
3605         closure[closureSize++] = co;
3606         fifo[fifoSize++]       = cp;
3607         fifo[fifoSize++]       = co;
3608         fifo[fifoSize++]       = ct;
3609       }
3610     }
3611   }
3612   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3613   if (numPoints) *numPoints = closureSize / 2;
3614   if (points) *points = closure;
3615   PetscFunctionReturn(0);
3616 }
3617 
3618 /*@C
3619   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3620 
3621   Not collective
3622 
3623   Input Parameters:
3624 + dm      - The DMPlex
3625 . p       - The mesh point
3626 - useCone - PETSC_TRUE for the closure, otherwise return the star
3627 
3628   Input/Output Parameter:
3629 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3630            if NULL on input, internal storage will be returned, otherwise the provided array is used
3631 
3632   Output Parameter:
3633 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3634 
3635   Note:
3636   If using internal storage (points is NULL on input), each call overwrites the last output.
3637 
3638   Fortran Note:
3639   The numPoints argument is not present in the Fortran 90 binding since it is internal to the array.
3640 
3641   Level: beginner
3642 
3643 .seealso: `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3644 @*/
3645 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3646   PetscFunctionBeginHot;
3647   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3648   if (numPoints) PetscValidIntPointer(numPoints, 4);
3649   if (points) PetscValidPointer(points, 5);
3650   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3651   PetscFunctionReturn(0);
3652 }
3653 
3654 /*@C
3655   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3656 
3657   Not collective
3658 
3659   Input Parameters:
3660 + dm        - The DMPlex
3661 . p         - The mesh point
3662 . useCone   - PETSC_TRUE for the closure, otherwise return the star
3663 . numPoints - The number of points in the closure, so points[] is of size 2*numPoints
3664 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3665 
3666   Note:
3667   If not using internal storage (points is not NULL on input), this call is unnecessary
3668 
3669   Level: beginner
3670 
3671 .seealso: `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3672 @*/
3673 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[]) {
3674   PetscFunctionBeginHot;
3675   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3676   if (numPoints) *numPoints = 0;
3677   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3678   PetscFunctionReturn(0);
3679 }
3680 
3681 /*@
3682   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3683 
3684   Not collective
3685 
3686   Input Parameter:
3687 . mesh - The DMPlex
3688 
3689   Output Parameters:
3690 + maxConeSize - The maximum number of in-edges
3691 - maxSupportSize - The maximum number of out-edges
3692 
3693   Level: beginner
3694 
3695 .seealso: `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3696 @*/
3697 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize) {
3698   DM_Plex *mesh = (DM_Plex *)dm->data;
3699 
3700   PetscFunctionBegin;
3701   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3702   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3703   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3704   PetscFunctionReturn(0);
3705 }
3706 
3707 PetscErrorCode DMSetUp_Plex(DM dm) {
3708   DM_Plex *mesh = (DM_Plex *)dm->data;
3709   PetscInt size, maxSupportSize;
3710 
3711   PetscFunctionBegin;
3712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3713   PetscCall(PetscSectionSetUp(mesh->coneSection));
3714   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3715   PetscCall(PetscMalloc1(size, &mesh->cones));
3716   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3717   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3718   if (maxSupportSize) {
3719     PetscCall(PetscSectionSetUp(mesh->supportSection));
3720     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3721     PetscCall(PetscMalloc1(size, &mesh->supports));
3722   }
3723   PetscFunctionReturn(0);
3724 }
3725 
3726 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm) {
3727   PetscFunctionBegin;
3728   if (subdm) PetscCall(DMClone(dm, subdm));
3729   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3730   if (subdm) (*subdm)->useNatural = dm->useNatural;
3731   if (dm->useNatural && dm->sfMigration) {
3732     PetscSF sfNatural;
3733 
3734     (*subdm)->sfMigration = dm->sfMigration;
3735     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3736     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3737     (*subdm)->sfNatural = sfNatural;
3738   }
3739   PetscFunctionReturn(0);
3740 }
3741 
3742 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm) {
3743   PetscInt i = 0;
3744 
3745   PetscFunctionBegin;
3746   PetscCall(DMClone(dms[0], superdm));
3747   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3748   (*superdm)->useNatural = PETSC_FALSE;
3749   for (i = 0; i < len; i++) {
3750     if (dms[i]->useNatural && dms[i]->sfMigration) {
3751       PetscSF sfNatural;
3752 
3753       (*superdm)->sfMigration = dms[i]->sfMigration;
3754       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3755       (*superdm)->useNatural = PETSC_TRUE;
3756       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3757       (*superdm)->sfNatural = sfNatural;
3758       break;
3759     }
3760   }
3761   PetscFunctionReturn(0);
3762 }
3763 
3764 /*@
3765   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3766 
3767   Not collective
3768 
3769   Input Parameter:
3770 . mesh - The DMPlex
3771 
3772   Output Parameter:
3773 
3774   Note:
3775   This should be called after all calls to DMPlexSetCone()
3776 
3777   Level: beginner
3778 
3779 .seealso: `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
3780 @*/
3781 PetscErrorCode DMPlexSymmetrize(DM dm) {
3782   DM_Plex  *mesh = (DM_Plex *)dm->data;
3783   PetscInt *offsets;
3784   PetscInt  supportSize;
3785   PetscInt  pStart, pEnd, p;
3786 
3787   PetscFunctionBegin;
3788   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3789   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
3790   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
3791   /* Calculate support sizes */
3792   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3793   for (p = pStart; p < pEnd; ++p) {
3794     PetscInt dof, off, c;
3795 
3796     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3797     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3798     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
3799   }
3800   PetscCall(PetscSectionSetUp(mesh->supportSection));
3801   /* Calculate supports */
3802   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
3803   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
3804   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
3805   for (p = pStart; p < pEnd; ++p) {
3806     PetscInt dof, off, c;
3807 
3808     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3809     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3810     for (c = off; c < off + dof; ++c) {
3811       const PetscInt q = mesh->cones[c];
3812       PetscInt       offS;
3813 
3814       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
3815 
3816       mesh->supports[offS + offsets[q]] = p;
3817       ++offsets[q];
3818     }
3819   }
3820   PetscCall(PetscFree(offsets));
3821   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
3822   PetscFunctionReturn(0);
3823 }
3824 
3825 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd) {
3826   IS stratumIS;
3827 
3828   PetscFunctionBegin;
3829   if (pStart >= pEnd) PetscFunctionReturn(0);
3830   if (PetscDefined(USE_DEBUG)) {
3831     PetscInt  qStart, qEnd, numLevels, level;
3832     PetscBool overlap = PETSC_FALSE;
3833     PetscCall(DMLabelGetNumValues(label, &numLevels));
3834     for (level = 0; level < numLevels; level++) {
3835       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3836       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
3837         overlap = PETSC_TRUE;
3838         break;
3839       }
3840     }
3841     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);
3842   }
3843   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
3844   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
3845   PetscCall(ISDestroy(&stratumIS));
3846   PetscFunctionReturn(0);
3847 }
3848 
3849 /*@
3850   DMPlexStratify - The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
3851   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram). The strata group all points of the
3852   same grade, and this function calculates the strata. This grade can be seen as the height (or depth) of the point in
3853   the DAG.
3854 
3855   Collective on dm
3856 
3857   Input Parameter:
3858 . mesh - The DMPlex
3859 
3860   Output Parameter:
3861 
3862   Notes:
3863   Concretely, DMPlexStratify() creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
3864   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
3865   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through DMPlexGetDepthLabel() or DMPlexGetDepthStratum(), or
3866   manually via DMGetLabel().  The height is defined implicitly by height = maxDimension - depth, and can be accessed
3867   via DMPlexGetHeightStratum().  For example, cells have height 0 and faces have height 1.
3868 
3869   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
3870   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
3871   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
3872   to interpolate only that one (e0), so that
3873 $  cone(c0) = {e0, v2}
3874 $  cone(e0) = {v0, v1}
3875   If DMPlexStratify() is run on this mesh, it will give depths
3876 $  depth 0 = {v0, v1, v2}
3877 $  depth 1 = {e0, c0}
3878   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
3879 
3880   DMPlexStratify() should be called after all calls to DMPlexSymmetrize()
3881 
3882   Level: beginner
3883 
3884 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
3885 @*/
3886 PetscErrorCode DMPlexStratify(DM dm) {
3887   DM_Plex *mesh = (DM_Plex *)dm->data;
3888   DMLabel  label;
3889   PetscInt pStart, pEnd, p;
3890   PetscInt numRoots = 0, numLeaves = 0;
3891 
3892   PetscFunctionBegin;
3893   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3894   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
3895 
3896   /* Create depth label */
3897   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3898   PetscCall(DMCreateLabel(dm, "depth"));
3899   PetscCall(DMPlexGetDepthLabel(dm, &label));
3900 
3901   {
3902     /* Initialize roots and count leaves */
3903     PetscInt sMin = PETSC_MAX_INT;
3904     PetscInt sMax = PETSC_MIN_INT;
3905     PetscInt coneSize, supportSize;
3906 
3907     for (p = pStart; p < pEnd; ++p) {
3908       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3909       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3910       if (!coneSize && supportSize) {
3911         sMin = PetscMin(p, sMin);
3912         sMax = PetscMax(p, sMax);
3913         ++numRoots;
3914       } else if (!supportSize && coneSize) {
3915         ++numLeaves;
3916       } else if (!supportSize && !coneSize) {
3917         /* Isolated points */
3918         sMin = PetscMin(p, sMin);
3919         sMax = PetscMax(p, sMax);
3920       }
3921     }
3922     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
3923   }
3924 
3925   if (numRoots + numLeaves == (pEnd - pStart)) {
3926     PetscInt sMin = PETSC_MAX_INT;
3927     PetscInt sMax = PETSC_MIN_INT;
3928     PetscInt coneSize, supportSize;
3929 
3930     for (p = pStart; p < pEnd; ++p) {
3931       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3932       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
3933       if (!supportSize && coneSize) {
3934         sMin = PetscMin(p, sMin);
3935         sMax = PetscMax(p, sMax);
3936       }
3937     }
3938     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
3939   } else {
3940     PetscInt level = 0;
3941     PetscInt qStart, qEnd, q;
3942 
3943     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3944     while (qEnd > qStart) {
3945       PetscInt sMin = PETSC_MAX_INT;
3946       PetscInt sMax = PETSC_MIN_INT;
3947 
3948       for (q = qStart; q < qEnd; ++q) {
3949         const PetscInt *support;
3950         PetscInt        supportSize, s;
3951 
3952         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
3953         PetscCall(DMPlexGetSupport(dm, q, &support));
3954         for (s = 0; s < supportSize; ++s) {
3955           sMin = PetscMin(support[s], sMin);
3956           sMax = PetscMax(support[s], sMax);
3957         }
3958       }
3959       PetscCall(DMLabelGetNumValues(label, &level));
3960       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
3961       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
3962     }
3963   }
3964   { /* just in case there is an empty process */
3965     PetscInt numValues, maxValues = 0, v;
3966 
3967     PetscCall(DMLabelGetNumValues(label, &numValues));
3968     PetscCallMPI(MPI_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
3969     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
3970   }
3971   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
3972   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
3973   PetscFunctionReturn(0);
3974 }
3975 
3976 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt) {
3977   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
3978   PetscInt       dim, depth, pheight, coneSize;
3979 
3980   PetscFunctionBeginHot;
3981   PetscCall(DMGetDimension(dm, &dim));
3982   PetscCall(DMPlexGetDepth(dm, &depth));
3983   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3984   pheight = depth - pdepth;
3985   if (depth <= 1) {
3986     switch (pdepth) {
3987     case 0: ct = DM_POLYTOPE_POINT; break;
3988     case 1:
3989       switch (coneSize) {
3990       case 2: ct = DM_POLYTOPE_SEGMENT; break;
3991       case 3: ct = DM_POLYTOPE_TRIANGLE; break;
3992       case 4:
3993         switch (dim) {
3994         case 2: ct = DM_POLYTOPE_QUADRILATERAL; break;
3995         case 3: ct = DM_POLYTOPE_TETRAHEDRON; break;
3996         default: break;
3997         }
3998         break;
3999       case 5: ct = DM_POLYTOPE_PYRAMID; break;
4000       case 6: ct = DM_POLYTOPE_TRI_PRISM_TENSOR; break;
4001       case 8: ct = DM_POLYTOPE_HEXAHEDRON; break;
4002       default: break;
4003       }
4004     }
4005   } else {
4006     if (pdepth == 0) {
4007       ct = DM_POLYTOPE_POINT;
4008     } else if (pheight == 0) {
4009       switch (dim) {
4010       case 1:
4011         switch (coneSize) {
4012         case 2: ct = DM_POLYTOPE_SEGMENT; break;
4013         default: break;
4014         }
4015         break;
4016       case 2:
4017         switch (coneSize) {
4018         case 3: ct = DM_POLYTOPE_TRIANGLE; break;
4019         case 4: ct = DM_POLYTOPE_QUADRILATERAL; break;
4020         default: break;
4021         }
4022         break;
4023       case 3:
4024         switch (coneSize) {
4025         case 4: ct = DM_POLYTOPE_TETRAHEDRON; break;
4026         case 5: {
4027           const PetscInt *cone;
4028           PetscInt        faceConeSize;
4029 
4030           PetscCall(DMPlexGetCone(dm, p, &cone));
4031           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4032           switch (faceConeSize) {
4033           case 3: ct = DM_POLYTOPE_TRI_PRISM_TENSOR; break;
4034           case 4: ct = DM_POLYTOPE_PYRAMID; break;
4035           }
4036         } break;
4037         case 6: ct = DM_POLYTOPE_HEXAHEDRON; break;
4038         default: break;
4039         }
4040         break;
4041       default: break;
4042       }
4043     } else if (pheight > 0) {
4044       switch (coneSize) {
4045       case 2: ct = DM_POLYTOPE_SEGMENT; break;
4046       case 3: ct = DM_POLYTOPE_TRIANGLE; break;
4047       case 4: ct = DM_POLYTOPE_QUADRILATERAL; break;
4048       default: break;
4049       }
4050     }
4051   }
4052   *pt = ct;
4053   PetscFunctionReturn(0);
4054 }
4055 
4056 /*@
4057   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4058 
4059   Collective on dm
4060 
4061   Input Parameter:
4062 . mesh - The DMPlex
4063 
4064   DMPlexComputeCellTypes() should be called after all calls to DMPlexSymmetrize() and DMPlexStratify()
4065 
4066   Level: developer
4067 
4068   Note: This function is normally called automatically by Plex when a cell type is requested. It creates an
4069   internal DMLabel named "celltype" which can be directly accessed using DMGetLabel(). A user may disable
4070   automatic creation by creating the label manually, using DMCreateLabel(dm, "celltype").
4071 
4072 .seealso: `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4073 @*/
4074 PetscErrorCode DMPlexComputeCellTypes(DM dm) {
4075   DM_Plex *mesh;
4076   DMLabel  ctLabel;
4077   PetscInt pStart, pEnd, p;
4078 
4079   PetscFunctionBegin;
4080   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4081   mesh = (DM_Plex *)dm->data;
4082   PetscCall(DMCreateLabel(dm, "celltype"));
4083   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4084   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4085   for (p = pStart; p < pEnd; ++p) {
4086     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4087     PetscInt       pdepth;
4088 
4089     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4090     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4091     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4092     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4093   }
4094   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4095   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4096   PetscFunctionReturn(0);
4097 }
4098 
4099 /*@C
4100   DMPlexGetJoin - Get an array for the join of the set of points
4101 
4102   Not Collective
4103 
4104   Input Parameters:
4105 + dm - The DMPlex object
4106 . numPoints - The number of input points for the join
4107 - points - The input points
4108 
4109   Output Parameters:
4110 + numCoveredPoints - The number of points in the join
4111 - coveredPoints - The points in the join
4112 
4113   Level: intermediate
4114 
4115   Note: Currently, this is restricted to a single level join
4116 
4117   Fortran Notes:
4118   Since it returns an array, this routine is only available in Fortran 90, and you must
4119   include petsc.h90 in your code.
4120 
4121   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4122 
4123 .seealso: `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4124 @*/
4125 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4126   DM_Plex  *mesh = (DM_Plex *)dm->data;
4127   PetscInt *join[2];
4128   PetscInt  joinSize, i = 0;
4129   PetscInt  dof, off, p, c, m;
4130   PetscInt  maxSupportSize;
4131 
4132   PetscFunctionBegin;
4133   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4134   PetscValidIntPointer(points, 3);
4135   PetscValidIntPointer(numCoveredPoints, 4);
4136   PetscValidPointer(coveredPoints, 5);
4137   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4138   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4139   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4140   /* Copy in support of first point */
4141   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4142   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4143   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4144   /* Check each successive support */
4145   for (p = 1; p < numPoints; ++p) {
4146     PetscInt newJoinSize = 0;
4147 
4148     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4149     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4150     for (c = 0; c < dof; ++c) {
4151       const PetscInt point = mesh->supports[off + c];
4152 
4153       for (m = 0; m < joinSize; ++m) {
4154         if (point == join[i][m]) {
4155           join[1 - i][newJoinSize++] = point;
4156           break;
4157         }
4158       }
4159     }
4160     joinSize = newJoinSize;
4161     i        = 1 - i;
4162   }
4163   *numCoveredPoints = joinSize;
4164   *coveredPoints    = join[i];
4165   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4166   PetscFunctionReturn(0);
4167 }
4168 
4169 /*@C
4170   DMPlexRestoreJoin - Restore an array for the join of the set of points
4171 
4172   Not Collective
4173 
4174   Input Parameters:
4175 + dm - The DMPlex object
4176 . numPoints - The number of input points for the join
4177 - points - The input points
4178 
4179   Output Parameters:
4180 + numCoveredPoints - The number of points in the join
4181 - coveredPoints - The points in the join
4182 
4183   Fortran Notes:
4184   Since it returns an array, this routine is only available in Fortran 90, and you must
4185   include petsc.h90 in your code.
4186 
4187   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4188 
4189   Level: intermediate
4190 
4191 .seealso: `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4192 @*/
4193 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4194   PetscFunctionBegin;
4195   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4196   if (points) PetscValidIntPointer(points, 3);
4197   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4198   PetscValidPointer(coveredPoints, 5);
4199   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4200   if (numCoveredPoints) *numCoveredPoints = 0;
4201   PetscFunctionReturn(0);
4202 }
4203 
4204 /*@C
4205   DMPlexGetFullJoin - Get an array for the join of the set of points
4206 
4207   Not Collective
4208 
4209   Input Parameters:
4210 + dm - The DMPlex object
4211 . numPoints - The number of input points for the join
4212 - points - The input points
4213 
4214   Output Parameters:
4215 + numCoveredPoints - The number of points in the join
4216 - coveredPoints - The points in the join
4217 
4218   Fortran Notes:
4219   Since it returns an array, this routine is only available in Fortran 90, and you must
4220   include petsc.h90 in your code.
4221 
4222   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4223 
4224   Level: intermediate
4225 
4226 .seealso: `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4227 @*/
4228 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4229   PetscInt *offsets, **closures;
4230   PetscInt *join[2];
4231   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4232   PetscInt  p, d, c, m, ms;
4233 
4234   PetscFunctionBegin;
4235   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4236   PetscValidIntPointer(points, 3);
4237   PetscValidIntPointer(numCoveredPoints, 4);
4238   PetscValidPointer(coveredPoints, 5);
4239 
4240   PetscCall(DMPlexGetDepth(dm, &depth));
4241   PetscCall(PetscCalloc1(numPoints, &closures));
4242   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4243   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4244   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4245   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4246   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4247 
4248   for (p = 0; p < numPoints; ++p) {
4249     PetscInt closureSize;
4250 
4251     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4252 
4253     offsets[p * (depth + 2) + 0] = 0;
4254     for (d = 0; d < depth + 1; ++d) {
4255       PetscInt pStart, pEnd, i;
4256 
4257       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4258       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4259         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4260           offsets[p * (depth + 2) + d + 1] = i;
4261           break;
4262         }
4263       }
4264       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4265     }
4266     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);
4267   }
4268   for (d = 0; d < depth + 1; ++d) {
4269     PetscInt dof;
4270 
4271     /* Copy in support of first point */
4272     dof = offsets[d + 1] - offsets[d];
4273     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4274     /* Check each successive cone */
4275     for (p = 1; p < numPoints && joinSize; ++p) {
4276       PetscInt newJoinSize = 0;
4277 
4278       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4279       for (c = 0; c < dof; ++c) {
4280         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4281 
4282         for (m = 0; m < joinSize; ++m) {
4283           if (point == join[i][m]) {
4284             join[1 - i][newJoinSize++] = point;
4285             break;
4286           }
4287         }
4288       }
4289       joinSize = newJoinSize;
4290       i        = 1 - i;
4291     }
4292     if (joinSize) break;
4293   }
4294   *numCoveredPoints = joinSize;
4295   *coveredPoints    = join[i];
4296   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4297   PetscCall(PetscFree(closures));
4298   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4299   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4300   PetscFunctionReturn(0);
4301 }
4302 
4303 /*@C
4304   DMPlexGetMeet - Get an array for the meet of the set of points
4305 
4306   Not Collective
4307 
4308   Input Parameters:
4309 + dm - The DMPlex object
4310 . numPoints - The number of input points for the meet
4311 - points - The input points
4312 
4313   Output Parameters:
4314 + numCoveredPoints - The number of points in the meet
4315 - coveredPoints - The points in the meet
4316 
4317   Level: intermediate
4318 
4319   Note: Currently, this is restricted to a single level meet
4320 
4321   Fortran Notes:
4322   Since it returns an array, this routine is only available in Fortran 90, and you must
4323   include petsc.h90 in your code.
4324 
4325   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4326 
4327 .seealso: `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4328 @*/
4329 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints) {
4330   DM_Plex  *mesh = (DM_Plex *)dm->data;
4331   PetscInt *meet[2];
4332   PetscInt  meetSize, i = 0;
4333   PetscInt  dof, off, p, c, m;
4334   PetscInt  maxConeSize;
4335 
4336   PetscFunctionBegin;
4337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4338   PetscValidIntPointer(points, 3);
4339   PetscValidIntPointer(numCoveringPoints, 4);
4340   PetscValidPointer(coveringPoints, 5);
4341   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4342   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4343   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4344   /* Copy in cone of first point */
4345   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4346   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4347   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4348   /* Check each successive cone */
4349   for (p = 1; p < numPoints; ++p) {
4350     PetscInt newMeetSize = 0;
4351 
4352     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4353     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4354     for (c = 0; c < dof; ++c) {
4355       const PetscInt point = mesh->cones[off + c];
4356 
4357       for (m = 0; m < meetSize; ++m) {
4358         if (point == meet[i][m]) {
4359           meet[1 - i][newMeetSize++] = point;
4360           break;
4361         }
4362       }
4363     }
4364     meetSize = newMeetSize;
4365     i        = 1 - i;
4366   }
4367   *numCoveringPoints = meetSize;
4368   *coveringPoints    = meet[i];
4369   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4370   PetscFunctionReturn(0);
4371 }
4372 
4373 /*@C
4374   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4375 
4376   Not Collective
4377 
4378   Input Parameters:
4379 + dm - The DMPlex object
4380 . numPoints - The number of input points for the meet
4381 - points - The input points
4382 
4383   Output Parameters:
4384 + numCoveredPoints - The number of points in the meet
4385 - coveredPoints - The points in the meet
4386 
4387   Level: intermediate
4388 
4389   Fortran Notes:
4390   Since it returns an array, this routine is only available in Fortran 90, and you must
4391   include petsc.h90 in your code.
4392 
4393   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4394 
4395 .seealso: `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4396 @*/
4397 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4398   PetscFunctionBegin;
4399   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4400   if (points) PetscValidIntPointer(points, 3);
4401   if (numCoveredPoints) PetscValidIntPointer(numCoveredPoints, 4);
4402   PetscValidPointer(coveredPoints, 5);
4403   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4404   if (numCoveredPoints) *numCoveredPoints = 0;
4405   PetscFunctionReturn(0);
4406 }
4407 
4408 /*@C
4409   DMPlexGetFullMeet - Get an array for the meet of the set of points
4410 
4411   Not Collective
4412 
4413   Input Parameters:
4414 + dm - The DMPlex object
4415 . numPoints - The number of input points for the meet
4416 - points - The input points
4417 
4418   Output Parameters:
4419 + numCoveredPoints - The number of points in the meet
4420 - coveredPoints - The points in the meet
4421 
4422   Level: intermediate
4423 
4424   Fortran Notes:
4425   Since it returns an array, this routine is only available in Fortran 90, and you must
4426   include petsc.h90 in your code.
4427 
4428   The numCoveredPoints argument is not present in the Fortran 90 binding since it is internal to the array.
4429 
4430 .seealso: `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4431 @*/
4432 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints) {
4433   PetscInt *offsets, **closures;
4434   PetscInt *meet[2];
4435   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4436   PetscInt  p, h, c, m, mc;
4437 
4438   PetscFunctionBegin;
4439   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4440   PetscValidIntPointer(points, 3);
4441   PetscValidIntPointer(numCoveredPoints, 4);
4442   PetscValidPointer(coveredPoints, 5);
4443 
4444   PetscCall(DMPlexGetDepth(dm, &height));
4445   PetscCall(PetscMalloc1(numPoints, &closures));
4446   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4447   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4448   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4449   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4450   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4451 
4452   for (p = 0; p < numPoints; ++p) {
4453     PetscInt closureSize;
4454 
4455     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4456 
4457     offsets[p * (height + 2) + 0] = 0;
4458     for (h = 0; h < height + 1; ++h) {
4459       PetscInt pStart, pEnd, i;
4460 
4461       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4462       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4463         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4464           offsets[p * (height + 2) + h + 1] = i;
4465           break;
4466         }
4467       }
4468       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4469     }
4470     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);
4471   }
4472   for (h = 0; h < height + 1; ++h) {
4473     PetscInt dof;
4474 
4475     /* Copy in cone of first point */
4476     dof = offsets[h + 1] - offsets[h];
4477     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4478     /* Check each successive cone */
4479     for (p = 1; p < numPoints && meetSize; ++p) {
4480       PetscInt newMeetSize = 0;
4481 
4482       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4483       for (c = 0; c < dof; ++c) {
4484         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4485 
4486         for (m = 0; m < meetSize; ++m) {
4487           if (point == meet[i][m]) {
4488             meet[1 - i][newMeetSize++] = point;
4489             break;
4490           }
4491         }
4492       }
4493       meetSize = newMeetSize;
4494       i        = 1 - i;
4495     }
4496     if (meetSize) break;
4497   }
4498   *numCoveredPoints = meetSize;
4499   *coveredPoints    = meet[i];
4500   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4501   PetscCall(PetscFree(closures));
4502   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4503   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4504   PetscFunctionReturn(0);
4505 }
4506 
4507 /*@C
4508   DMPlexEqual - Determine if two DMs have the same topology
4509 
4510   Not Collective
4511 
4512   Input Parameters:
4513 + dmA - A DMPlex object
4514 - dmB - A DMPlex object
4515 
4516   Output Parameters:
4517 . equal - PETSC_TRUE if the topologies are identical
4518 
4519   Level: intermediate
4520 
4521   Notes:
4522   We are not solving graph isomorphism, so we do not permutation.
4523 
4524 .seealso: `DMPlexGetCone()`
4525 @*/
4526 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal) {
4527   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4528 
4529   PetscFunctionBegin;
4530   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4531   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4532   PetscValidBoolPointer(equal, 3);
4533 
4534   *equal = PETSC_FALSE;
4535   PetscCall(DMPlexGetDepth(dmA, &depth));
4536   PetscCall(DMPlexGetDepth(dmB, &depthB));
4537   if (depth != depthB) PetscFunctionReturn(0);
4538   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4539   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4540   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(0);
4541   for (p = pStart; p < pEnd; ++p) {
4542     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4543     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4544 
4545     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4546     PetscCall(DMPlexGetCone(dmA, p, &cone));
4547     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4548     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4549     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4550     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4551     if (coneSize != coneSizeB) PetscFunctionReturn(0);
4552     for (c = 0; c < coneSize; ++c) {
4553       if (cone[c] != coneB[c]) PetscFunctionReturn(0);
4554       if (ornt[c] != orntB[c]) PetscFunctionReturn(0);
4555     }
4556     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4557     PetscCall(DMPlexGetSupport(dmA, p, &support));
4558     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4559     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4560     if (supportSize != supportSizeB) PetscFunctionReturn(0);
4561     for (s = 0; s < supportSize; ++s) {
4562       if (support[s] != supportB[s]) PetscFunctionReturn(0);
4563     }
4564   }
4565   *equal = PETSC_TRUE;
4566   PetscFunctionReturn(0);
4567 }
4568 
4569 /*@C
4570   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4571 
4572   Not Collective
4573 
4574   Input Parameters:
4575 + dm         - The DMPlex
4576 . cellDim    - The cell dimension
4577 - numCorners - The number of vertices on a cell
4578 
4579   Output Parameters:
4580 . numFaceVertices - The number of vertices on a face
4581 
4582   Level: developer
4583 
4584   Notes:
4585   Of course this can only work for a restricted set of symmetric shapes
4586 
4587 .seealso: `DMPlexGetCone()`
4588 @*/
4589 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices) {
4590   MPI_Comm comm;
4591 
4592   PetscFunctionBegin;
4593   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4594   PetscValidIntPointer(numFaceVertices, 4);
4595   switch (cellDim) {
4596   case 0: *numFaceVertices = 0; break;
4597   case 1: *numFaceVertices = 1; break;
4598   case 2:
4599     switch (numCorners) {
4600     case 3:                 /* triangle */
4601       *numFaceVertices = 2; /* Edge has 2 vertices */
4602       break;
4603     case 4:                 /* quadrilateral */
4604       *numFaceVertices = 2; /* Edge has 2 vertices */
4605       break;
4606     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4607       *numFaceVertices = 3; /* Edge has 3 vertices */
4608       break;
4609     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4610       *numFaceVertices = 3; /* Edge has 3 vertices */
4611       break;
4612     default: SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4613     }
4614     break;
4615   case 3:
4616     switch (numCorners) {
4617     case 4:                 /* tetradehdron */
4618       *numFaceVertices = 3; /* Face has 3 vertices */
4619       break;
4620     case 6:                 /* tet cohesive cells */
4621       *numFaceVertices = 4; /* Face has 4 vertices */
4622       break;
4623     case 8:                 /* hexahedron */
4624       *numFaceVertices = 4; /* Face has 4 vertices */
4625       break;
4626     case 9:                 /* tet cohesive Lagrange cells */
4627       *numFaceVertices = 6; /* Face has 6 vertices */
4628       break;
4629     case 10:                /* quadratic tetrahedron */
4630       *numFaceVertices = 6; /* Face has 6 vertices */
4631       break;
4632     case 12:                /* hex cohesive Lagrange cells */
4633       *numFaceVertices = 6; /* Face has 6 vertices */
4634       break;
4635     case 18:                /* quadratic tet cohesive Lagrange cells */
4636       *numFaceVertices = 6; /* Face has 6 vertices */
4637       break;
4638     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4639       *numFaceVertices = 9; /* Face has 9 vertices */
4640       break;
4641     default: SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4642     }
4643     break;
4644   default: SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4645   }
4646   PetscFunctionReturn(0);
4647 }
4648 
4649 /*@
4650   DMPlexGetDepthLabel - Get the DMLabel recording the depth of each point
4651 
4652   Not Collective
4653 
4654   Input Parameter:
4655 . dm    - The DMPlex object
4656 
4657   Output Parameter:
4658 . depthLabel - The DMLabel recording point depth
4659 
4660   Level: developer
4661 
4662 .seealso: `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4663 @*/
4664 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel) {
4665   PetscFunctionBegin;
4666   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4667   PetscValidPointer(depthLabel, 2);
4668   *depthLabel = dm->depthLabel;
4669   PetscFunctionReturn(0);
4670 }
4671 
4672 /*@
4673   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4674 
4675   Not Collective
4676 
4677   Input Parameter:
4678 . dm    - The DMPlex object
4679 
4680   Output Parameter:
4681 . depth - The number of strata (breadth first levels) in the DAG
4682 
4683   Level: developer
4684 
4685   Notes:
4686   This returns maximum of point depths over all points, i.e. maximum value of the label returned by DMPlexGetDepthLabel().
4687   The point depth is described more in detail in DMPlexGetDepthStratum().
4688   An empty mesh gives -1.
4689 
4690 .seealso: `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4691 @*/
4692 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth) {
4693   DMLabel  label;
4694   PetscInt d = 0;
4695 
4696   PetscFunctionBegin;
4697   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4698   PetscValidIntPointer(depth, 2);
4699   PetscCall(DMPlexGetDepthLabel(dm, &label));
4700   if (label) PetscCall(DMLabelGetNumValues(label, &d));
4701   *depth = d - 1;
4702   PetscFunctionReturn(0);
4703 }
4704 
4705 /*@
4706   DMPlexGetDepthStratum - Get the bounds [start, end) for all points at a certain depth.
4707 
4708   Not Collective
4709 
4710   Input Parameters:
4711 + dm    - The DMPlex object
4712 - depth - The requested depth
4713 
4714   Output Parameters:
4715 + start - The first point at this depth
4716 - end   - One beyond the last point at this depth
4717 
4718   Notes:
4719   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
4720   often "vertices".  If the mesh is "interpolated" (see DMPlexInterpolate()), then depth stratum 1 contains the next
4721   higher dimension, e.g., "edges".
4722 
4723   Level: developer
4724 
4725 .seealso: `DMPlexGetHeightStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
4726 @*/
4727 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end) {
4728   DMLabel  label;
4729   PetscInt pStart, pEnd;
4730 
4731   PetscFunctionBegin;
4732   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4733   if (start) {
4734     PetscValidIntPointer(start, 3);
4735     *start = 0;
4736   }
4737   if (end) {
4738     PetscValidIntPointer(end, 4);
4739     *end = 0;
4740   }
4741   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4742   if (pStart == pEnd) PetscFunctionReturn(0);
4743   if (depth < 0) {
4744     if (start) *start = pStart;
4745     if (end) *end = pEnd;
4746     PetscFunctionReturn(0);
4747   }
4748   PetscCall(DMPlexGetDepthLabel(dm, &label));
4749   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4750   PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
4751   PetscFunctionReturn(0);
4752 }
4753 
4754 /*@
4755   DMPlexGetHeightStratum - Get the bounds [start, end) for all points at a certain height.
4756 
4757   Not Collective
4758 
4759   Input Parameters:
4760 + dm     - The DMPlex object
4761 - height - The requested height
4762 
4763   Output Parameters:
4764 + start - The first point at this height
4765 - end   - One beyond the last point at this height
4766 
4767   Notes:
4768   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
4769   points, often called "cells" or "elements".  If the mesh is "interpolated" (see DMPlexInterpolate()), then height
4770   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
4771 
4772   Level: developer
4773 
4774 .seealso: `DMPlexGetDepthStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4775 @*/
4776 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end) {
4777   DMLabel  label;
4778   PetscInt depth, pStart, pEnd;
4779 
4780   PetscFunctionBegin;
4781   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4782   if (start) {
4783     PetscValidIntPointer(start, 3);
4784     *start = 0;
4785   }
4786   if (end) {
4787     PetscValidIntPointer(end, 4);
4788     *end = 0;
4789   }
4790   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4791   if (pStart == pEnd) PetscFunctionReturn(0);
4792   if (height < 0) {
4793     if (start) *start = pStart;
4794     if (end) *end = pEnd;
4795     PetscFunctionReturn(0);
4796   }
4797   PetscCall(DMPlexGetDepthLabel(dm, &label));
4798   PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
4799   PetscCall(DMLabelGetNumValues(label, &depth));
4800   PetscCall(DMLabelGetStratumBounds(label, depth - 1 - height, start, end));
4801   PetscFunctionReturn(0);
4802 }
4803 
4804 /*@
4805   DMPlexGetPointDepth - Get the depth of a given point
4806 
4807   Not Collective
4808 
4809   Input Parameters:
4810 + dm    - The DMPlex object
4811 - point - The point
4812 
4813   Output Parameter:
4814 . depth - The depth of the point
4815 
4816   Level: intermediate
4817 
4818 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
4819 @*/
4820 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth) {
4821   PetscFunctionBegin;
4822   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4823   PetscValidIntPointer(depth, 3);
4824   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
4825   PetscFunctionReturn(0);
4826 }
4827 
4828 /*@
4829   DMPlexGetPointHeight - Get the height of a given point
4830 
4831   Not Collective
4832 
4833   Input Parameters:
4834 + dm    - The DMPlex object
4835 - point - The point
4836 
4837   Output Parameter:
4838 . height - The height of the point
4839 
4840   Level: intermediate
4841 
4842 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
4843 @*/
4844 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height) {
4845   PetscInt n, pDepth;
4846 
4847   PetscFunctionBegin;
4848   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4849   PetscValidIntPointer(height, 3);
4850   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
4851   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
4852   *height = n - 1 - pDepth; /* DAG depth is n-1 */
4853   PetscFunctionReturn(0);
4854 }
4855 
4856 /*@
4857   DMPlexGetCellTypeLabel - Get the DMLabel recording the polytope type of each cell
4858 
4859   Not Collective
4860 
4861   Input Parameter:
4862 . dm - The DMPlex object
4863 
4864   Output Parameter:
4865 . celltypeLabel - The DMLabel recording cell polytope type
4866 
4867   Note: This function will trigger automatica computation of cell types. This can be disabled by calling
4868   DMCreateLabel(dm, "celltype") beforehand.
4869 
4870   Level: developer
4871 
4872 .seealso: `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
4873 @*/
4874 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel) {
4875   PetscFunctionBegin;
4876   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4877   PetscValidPointer(celltypeLabel, 2);
4878   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
4879   *celltypeLabel = dm->celltypeLabel;
4880   PetscFunctionReturn(0);
4881 }
4882 
4883 /*@
4884   DMPlexGetCellType - Get the polytope type of a given cell
4885 
4886   Not Collective
4887 
4888   Input Parameters:
4889 + dm   - The DMPlex object
4890 - cell - The cell
4891 
4892   Output Parameter:
4893 . celltype - The polytope type of the cell
4894 
4895   Level: intermediate
4896 
4897 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
4898 @*/
4899 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype) {
4900   DMLabel  label;
4901   PetscInt ct;
4902 
4903   PetscFunctionBegin;
4904   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4905   PetscValidPointer(celltype, 3);
4906   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4907   PetscCall(DMLabelGetValue(label, cell, &ct));
4908   PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
4909   *celltype = (DMPolytopeType)ct;
4910   PetscFunctionReturn(0);
4911 }
4912 
4913 /*@
4914   DMPlexSetCellType - Set the polytope type of a given cell
4915 
4916   Not Collective
4917 
4918   Input Parameters:
4919 + dm   - The DMPlex object
4920 . cell - The cell
4921 - celltype - The polytope type of the cell
4922 
4923   Note: By default, cell types will be automatically computed using DMPlexComputeCellTypes() before this function
4924   is executed. This function will override the computed type. However, if automatic classification will not succeed
4925   and a user wants to manually specify all types, the classification must be disabled by calling
4926   DMCreaateLabel(dm, "celltype") before getting or setting any cell types.
4927 
4928   Level: advanced
4929 
4930 .seealso: `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
4931 @*/
4932 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype) {
4933   DMLabel label;
4934 
4935   PetscFunctionBegin;
4936   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4937   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
4938   PetscCall(DMLabelSetValue(label, cell, celltype));
4939   PetscFunctionReturn(0);
4940 }
4941 
4942 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm) {
4943   PetscSection section, s;
4944   Mat          m;
4945   PetscInt     maxHeight;
4946 
4947   PetscFunctionBegin;
4948   PetscCall(DMClone(dm, cdm));
4949   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
4950   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
4951   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
4952   PetscCall(DMSetLocalSection(*cdm, section));
4953   PetscCall(PetscSectionDestroy(&section));
4954   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
4955   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
4956   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
4957   PetscCall(PetscSectionDestroy(&s));
4958   PetscCall(MatDestroy(&m));
4959 
4960   PetscCall(DMSetNumFields(*cdm, 1));
4961   PetscCall(DMCreateDS(*cdm));
4962   PetscFunctionReturn(0);
4963 }
4964 
4965 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field) {
4966   Vec coordsLocal, cellCoordsLocal;
4967   DM  coordsDM, cellCoordsDM;
4968 
4969   PetscFunctionBegin;
4970   *field = NULL;
4971   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
4972   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
4973   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
4974   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
4975   if (coordsLocal && coordsDM) {
4976     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
4977     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
4978   }
4979   PetscFunctionReturn(0);
4980 }
4981 
4982 /*@C
4983   DMPlexGetConeSection - Return a section which describes the layout of cone data
4984 
4985   Not Collective
4986 
4987   Input Parameters:
4988 . dm        - The DMPlex object
4989 
4990   Output Parameter:
4991 . section - The PetscSection object
4992 
4993   Level: developer
4994 
4995 .seealso: `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`
4996 @*/
4997 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section) {
4998   DM_Plex *mesh = (DM_Plex *)dm->data;
4999 
5000   PetscFunctionBegin;
5001   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5002   if (section) *section = mesh->coneSection;
5003   PetscFunctionReturn(0);
5004 }
5005 
5006 /*@C
5007   DMPlexGetSupportSection - Return a section which describes the layout of support data
5008 
5009   Not Collective
5010 
5011   Input Parameters:
5012 . dm        - The DMPlex object
5013 
5014   Output Parameter:
5015 . section - The PetscSection object
5016 
5017   Level: developer
5018 
5019 .seealso: `DMPlexGetConeSection()`
5020 @*/
5021 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section) {
5022   DM_Plex *mesh = (DM_Plex *)dm->data;
5023 
5024   PetscFunctionBegin;
5025   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5026   if (section) *section = mesh->supportSection;
5027   PetscFunctionReturn(0);
5028 }
5029 
5030 /*@C
5031   DMPlexGetCones - Return cone data
5032 
5033   Not Collective
5034 
5035   Input Parameters:
5036 . dm        - The DMPlex object
5037 
5038   Output Parameter:
5039 . cones - The cone for each point
5040 
5041   Level: developer
5042 
5043 .seealso: `DMPlexGetConeSection()`
5044 @*/
5045 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[]) {
5046   DM_Plex *mesh = (DM_Plex *)dm->data;
5047 
5048   PetscFunctionBegin;
5049   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5050   if (cones) *cones = mesh->cones;
5051   PetscFunctionReturn(0);
5052 }
5053 
5054 /*@C
5055   DMPlexGetConeOrientations - Return cone orientation data
5056 
5057   Not Collective
5058 
5059   Input Parameters:
5060 . dm        - The DMPlex object
5061 
5062   Output Parameter:
5063 . coneOrientations - The array of cone orientations for all points
5064 
5065   Level: developer
5066 
5067   Notes:
5068   The PetscSection returned by DMPlexGetConeSection() partitions coneOrientations into cone orientations of particular points as returned by DMPlexGetConeOrientation().
5069 
5070   The meaning of coneOrientations values is detailed in DMPlexGetConeOrientation().
5071 
5072 .seealso: `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`
5073 @*/
5074 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[]) {
5075   DM_Plex *mesh = (DM_Plex *)dm->data;
5076 
5077   PetscFunctionBegin;
5078   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5079   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5080   PetscFunctionReturn(0);
5081 }
5082 
5083 /******************************** FEM Support **********************************/
5084 
5085 /*
5086  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5087  representing a line in the section.
5088 */
5089 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k) {
5090   PetscFunctionBeginHot;
5091   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5092   if (line < 0) {
5093     *k  = 0;
5094     *Nc = 0;
5095   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5096     *k = 1;
5097   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5098     /* An order k SEM disc has k-1 dofs on an edge */
5099     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5100     *k = *k / *Nc + 1;
5101   }
5102   PetscFunctionReturn(0);
5103 }
5104 
5105 /*@
5106 
5107   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5108   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5109   section provided (or the section of the DM).
5110 
5111   Input Parameters:
5112 + dm      - The DM
5113 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or PETSC_DETERMINE
5114 - section - The PetscSection to reorder, or NULL for the default section
5115 
5116   Note: The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5117   degree of the basis.
5118 
5119   Example:
5120   A typical interpolated single-quad mesh might order points as
5121 .vb
5122   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5123 
5124   v4 -- e6 -- v3
5125   |           |
5126   e7    c0    e8
5127   |           |
5128   v1 -- e5 -- v2
5129 .ve
5130 
5131   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5132   dofs in the order of points, e.g.,
5133 .vb
5134     c0 -> [0,1,2,3]
5135     v1 -> [4]
5136     ...
5137     e5 -> [8, 9]
5138 .ve
5139 
5140   which corresponds to the dofs
5141 .vb
5142     6   10  11  7
5143     13  2   3   15
5144     12  0   1   14
5145     4   8   9   5
5146 .ve
5147 
5148   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5149 .vb
5150   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5151 .ve
5152 
5153   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5154 .vb
5155    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5156 .ve
5157 
5158   Level: developer
5159 
5160 .seealso: `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5161 @*/
5162 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section) {
5163   DMLabel   label;
5164   PetscInt  dim, depth = -1, eStart = -1, Nf;
5165   PetscBool vertexchart;
5166 
5167   PetscFunctionBegin;
5168   PetscCall(DMGetDimension(dm, &dim));
5169   if (dim < 1) PetscFunctionReturn(0);
5170   if (point < 0) {
5171     PetscInt sStart, sEnd;
5172 
5173     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5174     point = sEnd - sStart ? sStart : point;
5175   }
5176   PetscCall(DMPlexGetDepthLabel(dm, &label));
5177   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5178   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5179   if (depth == 1) {
5180     eStart = point;
5181   } else if (depth == dim) {
5182     const PetscInt *cone;
5183 
5184     PetscCall(DMPlexGetCone(dm, point, &cone));
5185     if (dim == 2) eStart = cone[0];
5186     else if (dim == 3) {
5187       const PetscInt *cone2;
5188       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5189       eStart = cone2[0];
5190     } 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);
5191   } 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);
5192   { /* Determine whether the chart covers all points or just vertices. */
5193     PetscInt pStart, pEnd, cStart, cEnd;
5194     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5195     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5196     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5197     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5198     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5199   }
5200   PetscCall(PetscSectionGetNumFields(section, &Nf));
5201   for (PetscInt d = 1; d <= dim; d++) {
5202     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5203     PetscInt *perm;
5204 
5205     for (f = 0; f < Nf; ++f) {
5206       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5207       size += PetscPowInt(k + 1, d) * Nc;
5208     }
5209     PetscCall(PetscMalloc1(size, &perm));
5210     for (f = 0; f < Nf; ++f) {
5211       switch (d) {
5212       case 1:
5213         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5214         /*
5215          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5216          We want              [ vtx0; edge of length k-1; vtx1 ]
5217          */
5218         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5219         for (i = 0; i < k - 1; i++)
5220           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5221         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5222         foffset = offset;
5223         break;
5224       case 2:
5225         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5226         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5227         /* The SEM order is
5228 
5229          v_lb, {e_b}, v_rb,
5230          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5231          v_lt, reverse {e_t}, v_rt
5232          */
5233         {
5234           const PetscInt of   = 0;
5235           const PetscInt oeb  = of + PetscSqr(k - 1);
5236           const PetscInt oer  = oeb + (k - 1);
5237           const PetscInt oet  = oer + (k - 1);
5238           const PetscInt oel  = oet + (k - 1);
5239           const PetscInt ovlb = oel + (k - 1);
5240           const PetscInt ovrb = ovlb + 1;
5241           const PetscInt ovrt = ovrb + 1;
5242           const PetscInt ovlt = ovrt + 1;
5243           PetscInt       o;
5244 
5245           /* bottom */
5246           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5247           for (o = oeb; o < oer; ++o)
5248             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5249           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5250           /* middle */
5251           for (i = 0; i < k - 1; ++i) {
5252             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5253             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5254               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5255             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5256           }
5257           /* top */
5258           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5259           for (o = oel - 1; o >= oet; --o)
5260             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5261           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5262           foffset = offset;
5263         }
5264         break;
5265       case 3:
5266         /* The original hex closure is
5267 
5268          {c,
5269          f_b, f_t, f_f, f_b, f_r, f_l,
5270          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5271          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5272          */
5273         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5274         /* The SEM order is
5275          Bottom Slice
5276          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5277          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5278          v_blb, {e_bb}, v_brb,
5279 
5280          Middle Slice (j)
5281          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5282          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5283          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5284 
5285          Top Slice
5286          v_tlf, {e_tf}, v_trf,
5287          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5288          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5289          */
5290         {
5291           const PetscInt oc    = 0;
5292           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5293           const PetscInt oft   = ofb + PetscSqr(k - 1);
5294           const PetscInt off   = oft + PetscSqr(k - 1);
5295           const PetscInt ofk   = off + PetscSqr(k - 1);
5296           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5297           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5298           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5299           const PetscInt oebb  = oebl + (k - 1);
5300           const PetscInt oebr  = oebb + (k - 1);
5301           const PetscInt oebf  = oebr + (k - 1);
5302           const PetscInt oetf  = oebf + (k - 1);
5303           const PetscInt oetr  = oetf + (k - 1);
5304           const PetscInt oetb  = oetr + (k - 1);
5305           const PetscInt oetl  = oetb + (k - 1);
5306           const PetscInt oerf  = oetl + (k - 1);
5307           const PetscInt oelf  = oerf + (k - 1);
5308           const PetscInt oelb  = oelf + (k - 1);
5309           const PetscInt oerb  = oelb + (k - 1);
5310           const PetscInt ovblf = oerb + (k - 1);
5311           const PetscInt ovblb = ovblf + 1;
5312           const PetscInt ovbrb = ovblb + 1;
5313           const PetscInt ovbrf = ovbrb + 1;
5314           const PetscInt ovtlf = ovbrf + 1;
5315           const PetscInt ovtrf = ovtlf + 1;
5316           const PetscInt ovtrb = ovtrf + 1;
5317           const PetscInt ovtlb = ovtrb + 1;
5318           PetscInt       o, n;
5319 
5320           /* Bottom Slice */
5321           /*   bottom */
5322           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5323           for (o = oetf - 1; o >= oebf; --o)
5324             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5325           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5326           /*   middle */
5327           for (i = 0; i < k - 1; ++i) {
5328             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5329             for (n = 0; n < k - 1; ++n) {
5330               o = ofb + n * (k - 1) + i;
5331               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5332             }
5333             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5334           }
5335           /*   top */
5336           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5337           for (o = oebb; o < oebr; ++o)
5338             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5339           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5340 
5341           /* Middle Slice */
5342           for (j = 0; j < k - 1; ++j) {
5343             /*   bottom */
5344             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5345             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5346               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5347             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5348             /*   middle */
5349             for (i = 0; i < k - 1; ++i) {
5350               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5351               for (n = 0; n < k - 1; ++n)
5352                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5353               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5354             }
5355             /*   top */
5356             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5357             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5358               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5359             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5360           }
5361 
5362           /* Top Slice */
5363           /*   bottom */
5364           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5365           for (o = oetf; o < oetr; ++o)
5366             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5367           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5368           /*   middle */
5369           for (i = 0; i < k - 1; ++i) {
5370             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5371             for (n = 0; n < k - 1; ++n)
5372               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5373             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5374           }
5375           /*   top */
5376           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5377           for (o = oetl - 1; o >= oetb; --o)
5378             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5379           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5380 
5381           foffset = offset;
5382         }
5383         break;
5384       default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5385       }
5386     }
5387     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5388     /* Check permutation */
5389     {
5390       PetscInt *check;
5391 
5392       PetscCall(PetscMalloc1(size, &check));
5393       for (i = 0; i < size; ++i) {
5394         check[i] = -1;
5395         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5396       }
5397       for (i = 0; i < size; ++i) check[perm[i]] = i;
5398       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5399       PetscCall(PetscFree(check));
5400     }
5401     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5402     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5403       PetscInt *loc_perm;
5404       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5405       for (PetscInt i = 0; i < size; i++) {
5406         loc_perm[i]        = perm[i];
5407         loc_perm[size + i] = size + perm[i];
5408       }
5409       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5410     }
5411   }
5412   PetscFunctionReturn(0);
5413 }
5414 
5415 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace) {
5416   PetscDS  prob;
5417   PetscInt depth, Nf, h;
5418   DMLabel  label;
5419 
5420   PetscFunctionBeginHot;
5421   PetscCall(DMGetDS(dm, &prob));
5422   Nf      = prob->Nf;
5423   label   = dm->depthLabel;
5424   *dspace = NULL;
5425   if (field < Nf) {
5426     PetscObject disc = prob->disc[field];
5427 
5428     if (disc->classid == PETSCFE_CLASSID) {
5429       PetscDualSpace dsp;
5430 
5431       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5432       PetscCall(DMLabelGetNumValues(label, &depth));
5433       PetscCall(DMLabelGetValue(label, point, &h));
5434       h = depth - 1 - h;
5435       if (h) {
5436         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5437       } else {
5438         *dspace = dsp;
5439       }
5440     }
5441   }
5442   PetscFunctionReturn(0);
5443 }
5444 
5445 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[]) {
5446   PetscScalar       *array;
5447   const PetscScalar *vArray;
5448   const PetscInt    *cone, *coneO;
5449   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5450 
5451   PetscFunctionBeginHot;
5452   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5453   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5454   PetscCall(DMPlexGetCone(dm, point, &cone));
5455   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5456   if (!values || !*values) {
5457     if ((point >= pStart) && (point < pEnd)) {
5458       PetscInt dof;
5459 
5460       PetscCall(PetscSectionGetDof(section, point, &dof));
5461       size += dof;
5462     }
5463     for (p = 0; p < numPoints; ++p) {
5464       const PetscInt cp = cone[p];
5465       PetscInt       dof;
5466 
5467       if ((cp < pStart) || (cp >= pEnd)) continue;
5468       PetscCall(PetscSectionGetDof(section, cp, &dof));
5469       size += dof;
5470     }
5471     if (!values) {
5472       if (csize) *csize = size;
5473       PetscFunctionReturn(0);
5474     }
5475     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5476   } else {
5477     array = *values;
5478   }
5479   size = 0;
5480   PetscCall(VecGetArrayRead(v, &vArray));
5481   if ((point >= pStart) && (point < pEnd)) {
5482     PetscInt           dof, off, d;
5483     const PetscScalar *varr;
5484 
5485     PetscCall(PetscSectionGetDof(section, point, &dof));
5486     PetscCall(PetscSectionGetOffset(section, point, &off));
5487     varr = &vArray[off];
5488     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5489     size += dof;
5490   }
5491   for (p = 0; p < numPoints; ++p) {
5492     const PetscInt     cp = cone[p];
5493     PetscInt           o  = coneO[p];
5494     PetscInt           dof, off, d;
5495     const PetscScalar *varr;
5496 
5497     if ((cp < pStart) || (cp >= pEnd)) continue;
5498     PetscCall(PetscSectionGetDof(section, cp, &dof));
5499     PetscCall(PetscSectionGetOffset(section, cp, &off));
5500     varr = &vArray[off];
5501     if (o >= 0) {
5502       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5503     } else {
5504       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5505     }
5506     size += dof;
5507   }
5508   PetscCall(VecRestoreArrayRead(v, &vArray));
5509   if (!*values) {
5510     if (csize) *csize = size;
5511     *values = array;
5512   } else {
5513     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5514     *csize = size;
5515   }
5516   PetscFunctionReturn(0);
5517 }
5518 
5519 /* Compress out points not in the section */
5520 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[]) {
5521   const PetscInt np = *numPoints;
5522   PetscInt       pStart, pEnd, p, q;
5523 
5524   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5525   for (p = 0, q = 0; p < np; ++p) {
5526     const PetscInt r = points[p * 2];
5527     if ((r >= pStart) && (r < pEnd)) {
5528       points[q * 2]     = r;
5529       points[q * 2 + 1] = points[p * 2 + 1];
5530       ++q;
5531     }
5532   }
5533   *numPoints = q;
5534   return 0;
5535 }
5536 
5537 /* Compressed closure does not apply closure permutation */
5538 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp) {
5539   const PetscInt *cla = NULL;
5540   PetscInt        np, *pts = NULL;
5541 
5542   PetscFunctionBeginHot;
5543   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5544   if (*clPoints) {
5545     PetscInt dof, off;
5546 
5547     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5548     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5549     PetscCall(ISGetIndices(*clPoints, &cla));
5550     np  = dof / 2;
5551     pts = (PetscInt *)&cla[off];
5552   } else {
5553     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &np, &pts));
5554     PetscCall(CompressPoints_Private(section, &np, pts));
5555   }
5556   *numPoints = np;
5557   *points    = pts;
5558   *clp       = cla;
5559   PetscFunctionReturn(0);
5560 }
5561 
5562 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp) {
5563   PetscFunctionBeginHot;
5564   if (!*clPoints) {
5565     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5566   } else {
5567     PetscCall(ISRestoreIndices(*clPoints, clp));
5568   }
5569   *numPoints = 0;
5570   *points    = NULL;
5571   *clSec     = NULL;
5572   *clPoints  = NULL;
5573   *clp       = NULL;
5574   PetscFunctionReturn(0);
5575 }
5576 
5577 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[]) {
5578   PetscInt            offset = 0, p;
5579   const PetscInt    **perms  = NULL;
5580   const PetscScalar **flips  = NULL;
5581 
5582   PetscFunctionBeginHot;
5583   *size = 0;
5584   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5585   for (p = 0; p < numPoints; p++) {
5586     const PetscInt     point = points[2 * p];
5587     const PetscInt    *perm  = perms ? perms[p] : NULL;
5588     const PetscScalar *flip  = flips ? flips[p] : NULL;
5589     PetscInt           dof, off, d;
5590     const PetscScalar *varr;
5591 
5592     PetscCall(PetscSectionGetDof(section, point, &dof));
5593     PetscCall(PetscSectionGetOffset(section, point, &off));
5594     varr = &vArray[off];
5595     if (clperm) {
5596       if (perm) {
5597         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5598       } else {
5599         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5600       }
5601       if (flip) {
5602         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5603       }
5604     } else {
5605       if (perm) {
5606         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5607       } else {
5608         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5609       }
5610       if (flip) {
5611         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5612       }
5613     }
5614     offset += dof;
5615   }
5616   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5617   *size = offset;
5618   PetscFunctionReturn(0);
5619 }
5620 
5621 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[]) {
5622   PetscInt offset = 0, f;
5623 
5624   PetscFunctionBeginHot;
5625   *size = 0;
5626   for (f = 0; f < numFields; ++f) {
5627     PetscInt            p;
5628     const PetscInt    **perms = NULL;
5629     const PetscScalar **flips = NULL;
5630 
5631     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5632     for (p = 0; p < numPoints; p++) {
5633       const PetscInt     point = points[2 * p];
5634       PetscInt           fdof, foff, b;
5635       const PetscScalar *varr;
5636       const PetscInt    *perm = perms ? perms[p] : NULL;
5637       const PetscScalar *flip = flips ? flips[p] : NULL;
5638 
5639       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5640       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5641       varr = &vArray[foff];
5642       if (clperm) {
5643         if (perm) {
5644           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
5645         } else {
5646           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
5647         }
5648         if (flip) {
5649           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
5650         }
5651       } else {
5652         if (perm) {
5653           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
5654         } else {
5655           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
5656         }
5657         if (flip) {
5658           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
5659         }
5660       }
5661       offset += fdof;
5662     }
5663     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5664   }
5665   *size = offset;
5666   PetscFunctionReturn(0);
5667 }
5668 
5669 /*@C
5670   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
5671 
5672   Not collective
5673 
5674   Input Parameters:
5675 + dm - The DM
5676 . section - The section describing the layout in v, or NULL to use the default section
5677 . v - The local vector
5678 - point - The point in the DM
5679 
5680   Input/Output Parameters:
5681 + csize  - The size of the input values array, or NULL; on output the number of values in the closure
5682 - values - An array to use for the values, or NULL to have it allocated automatically;
5683            if the user provided NULL, it is a borrowed array and should not be freed
5684 
5685 $ Note that DMPlexVecGetClosure/DMPlexVecRestoreClosure only allocates the values array if it set to NULL in the
5686 $ calling function. This is because DMPlexVecGetClosure() is typically called in the inner loop of a Vec or Mat
5687 $ assembly function, and a user may already have allocated storage for this operation.
5688 $
5689 $ A typical use could be
5690 $
5691 $  values = NULL;
5692 $  PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5693 $  for (cl = 0; cl < clSize; ++cl) {
5694 $    <Compute on closure>
5695 $  }
5696 $  PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
5697 $
5698 $ or
5699 $
5700 $  PetscMalloc1(clMaxSize, &values);
5701 $  for (p = pStart; p < pEnd; ++p) {
5702 $    clSize = clMaxSize;
5703 $    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
5704 $    for (cl = 0; cl < clSize; ++cl) {
5705 $      <Compute on closure>
5706 $    }
5707 $  }
5708 $  PetscFree(values);
5709 
5710   Fortran Notes:
5711   Since it returns an array, this routine is only available in Fortran 90, and you must
5712   include petsc.h90 in your code.
5713 
5714   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5715 
5716   Level: intermediate
5717 
5718 .seealso `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5719 @*/
5720 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[]) {
5721   PetscSection    clSection;
5722   IS              clPoints;
5723   PetscInt       *points = NULL;
5724   const PetscInt *clp, *perm;
5725   PetscInt        depth, numFields, numPoints, asize;
5726 
5727   PetscFunctionBeginHot;
5728   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5729   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5730   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5731   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5732   PetscCall(DMPlexGetDepth(dm, &depth));
5733   PetscCall(PetscSectionGetNumFields(section, &numFields));
5734   if (depth == 1 && numFields < 2) {
5735     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5736     PetscFunctionReturn(0);
5737   }
5738   /* Get points */
5739   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5740   /* Get sizes */
5741   asize = 0;
5742   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
5743     PetscInt dof;
5744     PetscCall(PetscSectionGetDof(section, points[p], &dof));
5745     asize += dof;
5746   }
5747   if (values) {
5748     const PetscScalar *vArray;
5749     PetscInt           size;
5750 
5751     if (*values) {
5752       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);
5753     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
5754     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
5755     PetscCall(VecGetArrayRead(v, &vArray));
5756     /* Get values */
5757     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
5758     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
5759     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
5760     /* Cleanup array */
5761     PetscCall(VecRestoreArrayRead(v, &vArray));
5762   }
5763   if (csize) *csize = asize;
5764   /* Cleanup points */
5765   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5766   PetscFunctionReturn(0);
5767 }
5768 
5769 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[]) {
5770   DMLabel            depthLabel;
5771   PetscSection       clSection;
5772   IS                 clPoints;
5773   PetscScalar       *array;
5774   const PetscScalar *vArray;
5775   PetscInt          *points = NULL;
5776   const PetscInt    *clp, *perm = NULL;
5777   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
5778 
5779   PetscFunctionBeginHot;
5780   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5781   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5782   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
5783   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
5784   PetscCall(DMPlexGetDepth(dm, &mdepth));
5785   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
5786   PetscCall(PetscSectionGetNumFields(section, &numFields));
5787   if (mdepth == 1 && numFields < 2) {
5788     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
5789     PetscFunctionReturn(0);
5790   }
5791   /* Get points */
5792   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5793   for (clsize = 0, p = 0; p < Np; p++) {
5794     PetscInt dof;
5795     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
5796     clsize += dof;
5797   }
5798   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
5799   /* Filter points */
5800   for (p = 0; p < numPoints * 2; p += 2) {
5801     PetscInt dep;
5802 
5803     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
5804     if (dep != depth) continue;
5805     points[Np * 2 + 0] = points[p];
5806     points[Np * 2 + 1] = points[p + 1];
5807     ++Np;
5808   }
5809   /* Get array */
5810   if (!values || !*values) {
5811     PetscInt asize = 0, dof;
5812 
5813     for (p = 0; p < Np * 2; p += 2) {
5814       PetscCall(PetscSectionGetDof(section, points[p], &dof));
5815       asize += dof;
5816     }
5817     if (!values) {
5818       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5819       if (csize) *csize = asize;
5820       PetscFunctionReturn(0);
5821     }
5822     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
5823   } else {
5824     array = *values;
5825   }
5826   PetscCall(VecGetArrayRead(v, &vArray));
5827   /* Get values */
5828   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
5829   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
5830   /* Cleanup points */
5831   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
5832   /* Cleanup array */
5833   PetscCall(VecRestoreArrayRead(v, &vArray));
5834   if (!*values) {
5835     if (csize) *csize = size;
5836     *values = array;
5837   } else {
5838     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5839     *csize = size;
5840   }
5841   PetscFunctionReturn(0);
5842 }
5843 
5844 /*@C
5845   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
5846 
5847   Not collective
5848 
5849   Input Parameters:
5850 + dm - The DM
5851 . section - The section describing the layout in v, or NULL to use the default section
5852 . v - The local vector
5853 . point - The point in the DM
5854 . csize - The number of values in the closure, or NULL
5855 - values - The array of values, which is a borrowed array and should not be freed
5856 
5857   Note that the array values are discarded and not copied back into v. In order to copy values back to v, use DMPlexVecSetClosure()
5858 
5859   Fortran Notes:
5860   Since it returns an array, this routine is only available in Fortran 90, and you must
5861   include petsc.h90 in your code.
5862 
5863   The csize argument is not present in the Fortran 90 binding since it is internal to the array.
5864 
5865   Level: intermediate
5866 
5867 .seealso `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
5868 @*/
5869 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[]) {
5870   PetscInt size = 0;
5871 
5872   PetscFunctionBegin;
5873   /* Should work without recalculating size */
5874   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
5875   *values = NULL;
5876   PetscFunctionReturn(0);
5877 }
5878 
5879 static inline void add(PetscScalar *x, PetscScalar y) {
5880   *x += y;
5881 }
5882 static inline void insert(PetscScalar *x, PetscScalar y) {
5883   *x = y;
5884 }
5885 
5886 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[]) {
5887   PetscInt        cdof;  /* The number of constraints on this point */
5888   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5889   PetscScalar    *a;
5890   PetscInt        off, cind = 0, k;
5891 
5892   PetscFunctionBegin;
5893   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5894   PetscCall(PetscSectionGetOffset(section, point, &off));
5895   a = &array[off];
5896   if (!cdof || setBC) {
5897     if (clperm) {
5898       if (perm) {
5899         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
5900       } else {
5901         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
5902       }
5903     } else {
5904       if (perm) {
5905         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
5906       } else {
5907         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
5908       }
5909     }
5910   } else {
5911     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5912     if (clperm) {
5913       if (perm) {
5914         for (k = 0; k < dof; ++k) {
5915           if ((cind < cdof) && (k == cdofs[cind])) {
5916             ++cind;
5917             continue;
5918           }
5919           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
5920         }
5921       } else {
5922         for (k = 0; k < dof; ++k) {
5923           if ((cind < cdof) && (k == cdofs[cind])) {
5924             ++cind;
5925             continue;
5926           }
5927           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
5928         }
5929       }
5930     } else {
5931       if (perm) {
5932         for (k = 0; k < dof; ++k) {
5933           if ((cind < cdof) && (k == cdofs[cind])) {
5934             ++cind;
5935             continue;
5936           }
5937           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
5938         }
5939       } else {
5940         for (k = 0; k < dof; ++k) {
5941           if ((cind < cdof) && (k == cdofs[cind])) {
5942             ++cind;
5943             continue;
5944           }
5945           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
5946         }
5947       }
5948     }
5949   }
5950   PetscFunctionReturn(0);
5951 }
5952 
5953 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[]) {
5954   PetscInt        cdof;  /* The number of constraints on this point */
5955   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
5956   PetscScalar    *a;
5957   PetscInt        off, cind = 0, k;
5958 
5959   PetscFunctionBegin;
5960   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
5961   PetscCall(PetscSectionGetOffset(section, point, &off));
5962   a = &array[off];
5963   if (cdof) {
5964     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
5965     if (clperm) {
5966       if (perm) {
5967         for (k = 0; k < dof; ++k) {
5968           if ((cind < cdof) && (k == cdofs[cind])) {
5969             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
5970             cind++;
5971           }
5972         }
5973       } else {
5974         for (k = 0; k < dof; ++k) {
5975           if ((cind < cdof) && (k == cdofs[cind])) {
5976             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
5977             cind++;
5978           }
5979         }
5980       }
5981     } else {
5982       if (perm) {
5983         for (k = 0; k < dof; ++k) {
5984           if ((cind < cdof) && (k == cdofs[cind])) {
5985             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
5986             cind++;
5987           }
5988         }
5989       } else {
5990         for (k = 0; k < dof; ++k) {
5991           if ((cind < cdof) && (k == cdofs[cind])) {
5992             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
5993             cind++;
5994           }
5995         }
5996       }
5997     }
5998   }
5999   PetscFunctionReturn(0);
6000 }
6001 
6002 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[]) {
6003   PetscScalar    *a;
6004   PetscInt        fdof, foff, fcdof, foffset = *offset;
6005   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6006   PetscInt        cind = 0, b;
6007 
6008   PetscFunctionBegin;
6009   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6010   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6011   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6012   a = &array[foff];
6013   if (!fcdof || setBC) {
6014     if (clperm) {
6015       if (perm) {
6016         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6017       } else {
6018         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6019       }
6020     } else {
6021       if (perm) {
6022         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6023       } else {
6024         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6025       }
6026     }
6027   } else {
6028     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6029     if (clperm) {
6030       if (perm) {
6031         for (b = 0; b < fdof; b++) {
6032           if ((cind < fcdof) && (b == fcdofs[cind])) {
6033             ++cind;
6034             continue;
6035           }
6036           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6037         }
6038       } else {
6039         for (b = 0; b < fdof; b++) {
6040           if ((cind < fcdof) && (b == fcdofs[cind])) {
6041             ++cind;
6042             continue;
6043           }
6044           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6045         }
6046       }
6047     } else {
6048       if (perm) {
6049         for (b = 0; b < fdof; b++) {
6050           if ((cind < fcdof) && (b == fcdofs[cind])) {
6051             ++cind;
6052             continue;
6053           }
6054           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6055         }
6056       } else {
6057         for (b = 0; b < fdof; b++) {
6058           if ((cind < fcdof) && (b == fcdofs[cind])) {
6059             ++cind;
6060             continue;
6061           }
6062           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6063         }
6064       }
6065     }
6066   }
6067   *offset += fdof;
6068   PetscFunctionReturn(0);
6069 }
6070 
6071 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[]) {
6072   PetscScalar    *a;
6073   PetscInt        fdof, foff, fcdof, foffset = *offset;
6074   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6075   PetscInt        Nc, cind = 0, ncind = 0, b;
6076   PetscBool       ncSet, fcSet;
6077 
6078   PetscFunctionBegin;
6079   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6080   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6081   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6082   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6083   a = &array[foff];
6084   if (fcdof) {
6085     /* We just override fcdof and fcdofs with Ncc and comps */
6086     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6087     if (clperm) {
6088       if (perm) {
6089         if (comps) {
6090           for (b = 0; b < fdof; b++) {
6091             ncSet = fcSet = PETSC_FALSE;
6092             if (b % Nc == comps[ncind]) {
6093               ncind = (ncind + 1) % Ncc;
6094               ncSet = PETSC_TRUE;
6095             }
6096             if ((cind < fcdof) && (b == fcdofs[cind])) {
6097               ++cind;
6098               fcSet = PETSC_TRUE;
6099             }
6100             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6101           }
6102         } else {
6103           for (b = 0; b < fdof; b++) {
6104             if ((cind < fcdof) && (b == fcdofs[cind])) {
6105               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6106               ++cind;
6107             }
6108           }
6109         }
6110       } else {
6111         if (comps) {
6112           for (b = 0; b < fdof; b++) {
6113             ncSet = fcSet = PETSC_FALSE;
6114             if (b % Nc == comps[ncind]) {
6115               ncind = (ncind + 1) % Ncc;
6116               ncSet = PETSC_TRUE;
6117             }
6118             if ((cind < fcdof) && (b == fcdofs[cind])) {
6119               ++cind;
6120               fcSet = PETSC_TRUE;
6121             }
6122             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6123           }
6124         } else {
6125           for (b = 0; b < fdof; b++) {
6126             if ((cind < fcdof) && (b == fcdofs[cind])) {
6127               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6128               ++cind;
6129             }
6130           }
6131         }
6132       }
6133     } else {
6134       if (perm) {
6135         if (comps) {
6136           for (b = 0; b < fdof; b++) {
6137             ncSet = fcSet = PETSC_FALSE;
6138             if (b % Nc == comps[ncind]) {
6139               ncind = (ncind + 1) % Ncc;
6140               ncSet = PETSC_TRUE;
6141             }
6142             if ((cind < fcdof) && (b == fcdofs[cind])) {
6143               ++cind;
6144               fcSet = PETSC_TRUE;
6145             }
6146             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6147           }
6148         } else {
6149           for (b = 0; b < fdof; b++) {
6150             if ((cind < fcdof) && (b == fcdofs[cind])) {
6151               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6152               ++cind;
6153             }
6154           }
6155         }
6156       } else {
6157         if (comps) {
6158           for (b = 0; b < fdof; b++) {
6159             ncSet = fcSet = PETSC_FALSE;
6160             if (b % Nc == comps[ncind]) {
6161               ncind = (ncind + 1) % Ncc;
6162               ncSet = PETSC_TRUE;
6163             }
6164             if ((cind < fcdof) && (b == fcdofs[cind])) {
6165               ++cind;
6166               fcSet = PETSC_TRUE;
6167             }
6168             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6169           }
6170         } else {
6171           for (b = 0; b < fdof; b++) {
6172             if ((cind < fcdof) && (b == fcdofs[cind])) {
6173               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6174               ++cind;
6175             }
6176           }
6177         }
6178       }
6179     }
6180   }
6181   *offset += fdof;
6182   PetscFunctionReturn(0);
6183 }
6184 
6185 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode) {
6186   PetscScalar    *array;
6187   const PetscInt *cone, *coneO;
6188   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6189 
6190   PetscFunctionBeginHot;
6191   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6192   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6193   PetscCall(DMPlexGetCone(dm, point, &cone));
6194   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6195   PetscCall(VecGetArray(v, &array));
6196   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6197     const PetscInt cp = !p ? point : cone[p - 1];
6198     const PetscInt o  = !p ? 0 : coneO[p - 1];
6199 
6200     if ((cp < pStart) || (cp >= pEnd)) {
6201       dof = 0;
6202       continue;
6203     }
6204     PetscCall(PetscSectionGetDof(section, cp, &dof));
6205     /* ADD_VALUES */
6206     {
6207       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6208       PetscScalar    *a;
6209       PetscInt        cdof, coff, cind = 0, k;
6210 
6211       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6212       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6213       a = &array[coff];
6214       if (!cdof) {
6215         if (o >= 0) {
6216           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6217         } else {
6218           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6219         }
6220       } else {
6221         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6222         if (o >= 0) {
6223           for (k = 0; k < dof; ++k) {
6224             if ((cind < cdof) && (k == cdofs[cind])) {
6225               ++cind;
6226               continue;
6227             }
6228             a[k] += values[off + k];
6229           }
6230         } else {
6231           for (k = 0; k < dof; ++k) {
6232             if ((cind < cdof) && (k == cdofs[cind])) {
6233               ++cind;
6234               continue;
6235             }
6236             a[k] += values[off + dof - k - 1];
6237           }
6238         }
6239       }
6240     }
6241   }
6242   PetscCall(VecRestoreArray(v, &array));
6243   PetscFunctionReturn(0);
6244 }
6245 
6246 /*@C
6247   DMPlexVecSetClosure - Set an array of the values on the closure of 'point'
6248 
6249   Not collective
6250 
6251   Input Parameters:
6252 + dm - The DM
6253 . section - The section describing the layout in v, or NULL to use the default section
6254 . v - The local vector
6255 . point - The point in the DM
6256 . values - The array of values
6257 - mode - The insert mode. One of INSERT_ALL_VALUES, ADD_ALL_VALUES, INSERT_VALUES, ADD_VALUES, INSERT_BC_VALUES, and ADD_BC_VALUES,
6258          where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions.
6259 
6260   Fortran Notes:
6261   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
6262 
6263   Level: intermediate
6264 
6265 .seealso `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6266 @*/
6267 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode) {
6268   PetscSection    clSection;
6269   IS              clPoints;
6270   PetscScalar    *array;
6271   PetscInt       *points = NULL;
6272   const PetscInt *clp, *clperm = NULL;
6273   PetscInt        depth, numFields, numPoints, p, clsize;
6274 
6275   PetscFunctionBeginHot;
6276   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6277   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6278   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6279   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6280   PetscCall(DMPlexGetDepth(dm, &depth));
6281   PetscCall(PetscSectionGetNumFields(section, &numFields));
6282   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6283     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6284     PetscFunctionReturn(0);
6285   }
6286   /* Get points */
6287   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6288   for (clsize = 0, p = 0; p < numPoints; p++) {
6289     PetscInt dof;
6290     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6291     clsize += dof;
6292   }
6293   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6294   /* Get array */
6295   PetscCall(VecGetArray(v, &array));
6296   /* Get values */
6297   if (numFields > 0) {
6298     PetscInt offset = 0, f;
6299     for (f = 0; f < numFields; ++f) {
6300       const PetscInt    **perms = NULL;
6301       const PetscScalar **flips = NULL;
6302 
6303       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6304       switch (mode) {
6305       case INSERT_VALUES:
6306         for (p = 0; p < numPoints; p++) {
6307           const PetscInt     point = points[2 * p];
6308           const PetscInt    *perm  = perms ? perms[p] : NULL;
6309           const PetscScalar *flip  = flips ? flips[p] : NULL;
6310           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array);
6311         }
6312         break;
6313       case INSERT_ALL_VALUES:
6314         for (p = 0; p < numPoints; p++) {
6315           const PetscInt     point = points[2 * p];
6316           const PetscInt    *perm  = perms ? perms[p] : NULL;
6317           const PetscScalar *flip  = flips ? flips[p] : NULL;
6318           updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array);
6319         }
6320         break;
6321       case INSERT_BC_VALUES:
6322         for (p = 0; p < numPoints; p++) {
6323           const PetscInt     point = points[2 * p];
6324           const PetscInt    *perm  = perms ? perms[p] : NULL;
6325           const PetscScalar *flip  = flips ? flips[p] : NULL;
6326           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array);
6327         }
6328         break;
6329       case ADD_VALUES:
6330         for (p = 0; p < numPoints; p++) {
6331           const PetscInt     point = points[2 * p];
6332           const PetscInt    *perm  = perms ? perms[p] : NULL;
6333           const PetscScalar *flip  = flips ? flips[p] : NULL;
6334           updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array);
6335         }
6336         break;
6337       case ADD_ALL_VALUES:
6338         for (p = 0; p < numPoints; p++) {
6339           const PetscInt     point = points[2 * p];
6340           const PetscInt    *perm  = perms ? perms[p] : NULL;
6341           const PetscScalar *flip  = flips ? flips[p] : NULL;
6342           updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array);
6343         }
6344         break;
6345       case ADD_BC_VALUES:
6346         for (p = 0; p < numPoints; p++) {
6347           const PetscInt     point = points[2 * p];
6348           const PetscInt    *perm  = perms ? perms[p] : NULL;
6349           const PetscScalar *flip  = flips ? flips[p] : NULL;
6350           updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array);
6351         }
6352         break;
6353       default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6354       }
6355       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6356     }
6357   } else {
6358     PetscInt            dof, off;
6359     const PetscInt    **perms = NULL;
6360     const PetscScalar **flips = NULL;
6361 
6362     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6363     switch (mode) {
6364     case INSERT_VALUES:
6365       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6366         const PetscInt     point = points[2 * p];
6367         const PetscInt    *perm  = perms ? perms[p] : NULL;
6368         const PetscScalar *flip  = flips ? flips[p] : NULL;
6369         PetscCall(PetscSectionGetDof(section, point, &dof));
6370         updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array);
6371       }
6372       break;
6373     case INSERT_ALL_VALUES:
6374       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6375         const PetscInt     point = points[2 * p];
6376         const PetscInt    *perm  = perms ? perms[p] : NULL;
6377         const PetscScalar *flip  = flips ? flips[p] : NULL;
6378         PetscCall(PetscSectionGetDof(section, point, &dof));
6379         updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array);
6380       }
6381       break;
6382     case INSERT_BC_VALUES:
6383       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6384         const PetscInt     point = points[2 * p];
6385         const PetscInt    *perm  = perms ? perms[p] : NULL;
6386         const PetscScalar *flip  = flips ? flips[p] : NULL;
6387         PetscCall(PetscSectionGetDof(section, point, &dof));
6388         updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array);
6389       }
6390       break;
6391     case ADD_VALUES:
6392       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6393         const PetscInt     point = points[2 * p];
6394         const PetscInt    *perm  = perms ? perms[p] : NULL;
6395         const PetscScalar *flip  = flips ? flips[p] : NULL;
6396         PetscCall(PetscSectionGetDof(section, point, &dof));
6397         updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array);
6398       }
6399       break;
6400     case ADD_ALL_VALUES:
6401       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6402         const PetscInt     point = points[2 * p];
6403         const PetscInt    *perm  = perms ? perms[p] : NULL;
6404         const PetscScalar *flip  = flips ? flips[p] : NULL;
6405         PetscCall(PetscSectionGetDof(section, point, &dof));
6406         updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array);
6407       }
6408       break;
6409     case ADD_BC_VALUES:
6410       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6411         const PetscInt     point = points[2 * p];
6412         const PetscInt    *perm  = perms ? perms[p] : NULL;
6413         const PetscScalar *flip  = flips ? flips[p] : NULL;
6414         PetscCall(PetscSectionGetDof(section, point, &dof));
6415         updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array);
6416       }
6417       break;
6418     default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6419     }
6420     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6421   }
6422   /* Cleanup points */
6423   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6424   /* Cleanup array */
6425   PetscCall(VecRestoreArray(v, &array));
6426   PetscFunctionReturn(0);
6427 }
6428 
6429 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6430 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains) {
6431   PetscFunctionBegin;
6432   *contains = PETSC_TRUE;
6433   if (label) {
6434     PetscInt fdof;
6435 
6436     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6437     if (!*contains) {
6438       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6439       *offset += fdof;
6440       PetscFunctionReturn(0);
6441     }
6442   }
6443   PetscFunctionReturn(0);
6444 }
6445 
6446 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6447 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) {
6448   PetscSection    clSection;
6449   IS              clPoints;
6450   PetscScalar    *array;
6451   PetscInt       *points = NULL;
6452   const PetscInt *clp;
6453   PetscInt        numFields, numPoints, p;
6454   PetscInt        offset = 0, f;
6455 
6456   PetscFunctionBeginHot;
6457   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6458   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6459   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6460   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6461   PetscCall(PetscSectionGetNumFields(section, &numFields));
6462   /* Get points */
6463   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6464   /* Get array */
6465   PetscCall(VecGetArray(v, &array));
6466   /* Get values */
6467   for (f = 0; f < numFields; ++f) {
6468     const PetscInt    **perms = NULL;
6469     const PetscScalar **flips = NULL;
6470     PetscBool           contains;
6471 
6472     if (!fieldActive[f]) {
6473       for (p = 0; p < numPoints * 2; p += 2) {
6474         PetscInt fdof;
6475         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6476         offset += fdof;
6477       }
6478       continue;
6479     }
6480     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6481     switch (mode) {
6482     case INSERT_VALUES:
6483       for (p = 0; p < numPoints; p++) {
6484         const PetscInt     point = points[2 * p];
6485         const PetscInt    *perm  = perms ? perms[p] : NULL;
6486         const PetscScalar *flip  = flips ? flips[p] : NULL;
6487         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6488         if (!contains) continue;
6489         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6490       }
6491       break;
6492     case INSERT_ALL_VALUES:
6493       for (p = 0; p < numPoints; p++) {
6494         const PetscInt     point = points[2 * p];
6495         const PetscInt    *perm  = perms ? perms[p] : NULL;
6496         const PetscScalar *flip  = flips ? flips[p] : NULL;
6497         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6498         if (!contains) continue;
6499         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6500       }
6501       break;
6502     case INSERT_BC_VALUES:
6503       for (p = 0; p < numPoints; p++) {
6504         const PetscInt     point = points[2 * p];
6505         const PetscInt    *perm  = perms ? perms[p] : NULL;
6506         const PetscScalar *flip  = flips ? flips[p] : NULL;
6507         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6508         if (!contains) continue;
6509         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6510       }
6511       break;
6512     case ADD_VALUES:
6513       for (p = 0; p < numPoints; p++) {
6514         const PetscInt     point = points[2 * p];
6515         const PetscInt    *perm  = perms ? perms[p] : NULL;
6516         const PetscScalar *flip  = flips ? flips[p] : NULL;
6517         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6518         if (!contains) continue;
6519         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6520       }
6521       break;
6522     case ADD_ALL_VALUES:
6523       for (p = 0; p < numPoints; p++) {
6524         const PetscInt     point = points[2 * p];
6525         const PetscInt    *perm  = perms ? perms[p] : NULL;
6526         const PetscScalar *flip  = flips ? flips[p] : NULL;
6527         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6528         if (!contains) continue;
6529         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6530       }
6531       break;
6532     default: SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6533     }
6534     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6535   }
6536   /* Cleanup points */
6537   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6538   /* Cleanup array */
6539   PetscCall(VecRestoreArray(v, &array));
6540   PetscFunctionReturn(0);
6541 }
6542 
6543 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[]) {
6544   PetscMPIInt rank;
6545   PetscInt    i, j;
6546 
6547   PetscFunctionBegin;
6548   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6549   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6550   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6551   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6552   numCIndices = numCIndices ? numCIndices : numRIndices;
6553   if (!values) PetscFunctionReturn(0);
6554   for (i = 0; i < numRIndices; i++) {
6555     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6556     for (j = 0; j < numCIndices; j++) {
6557 #if defined(PETSC_USE_COMPLEX)
6558       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6559 #else
6560       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6561 #endif
6562     }
6563     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6564   }
6565   PetscFunctionReturn(0);
6566 }
6567 
6568 /*
6569   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6570 
6571   Input Parameters:
6572 + section - The section for this data layout
6573 . islocal - Is the section (and thus indices being requested) local or global?
6574 . point   - The point contributing dofs with these indices
6575 . off     - The global offset of this point
6576 . loff    - The local offset of each field
6577 . setBC   - The flag determining whether to include indices of boundary values
6578 . perm    - A permutation of the dofs on this point, or NULL
6579 - indperm - A permutation of the entire indices array, or NULL
6580 
6581   Output Parameter:
6582 . indices - Indices for dofs on this point
6583 
6584   Level: developer
6585 
6586   Note: The indices could be local or global, depending on the value of 'off'.
6587 */
6588 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[]) {
6589   PetscInt        dof;   /* The number of unknowns on this point */
6590   PetscInt        cdof;  /* The number of constraints on this point */
6591   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6592   PetscInt        cind = 0, k;
6593 
6594   PetscFunctionBegin;
6595   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6596   PetscCall(PetscSectionGetDof(section, point, &dof));
6597   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6598   if (!cdof || setBC) {
6599     for (k = 0; k < dof; ++k) {
6600       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6601       const PetscInt ind    = indperm ? indperm[preind] : preind;
6602 
6603       indices[ind] = off + k;
6604     }
6605   } else {
6606     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6607     for (k = 0; k < dof; ++k) {
6608       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6609       const PetscInt ind    = indperm ? indperm[preind] : preind;
6610 
6611       if ((cind < cdof) && (k == cdofs[cind])) {
6612         /* Insert check for returning constrained indices */
6613         indices[ind] = -(off + k + 1);
6614         ++cind;
6615       } else {
6616         indices[ind] = off + k - (islocal ? 0 : cind);
6617       }
6618     }
6619   }
6620   *loff += dof;
6621   PetscFunctionReturn(0);
6622 }
6623 
6624 /*
6625  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
6626 
6627  Input Parameters:
6628 + section - a section (global or local)
6629 - islocal - PETSC_TRUE if requesting local indices (i.e., section is local); PETSC_FALSE for global
6630 . point - point within section
6631 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
6632 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
6633 . setBC - identify constrained (boundary condition) points via involution.
6634 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
6635 . permsoff - offset
6636 - indperm - index permutation
6637 
6638  Output Parameter:
6639 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
6640 . indices - array to hold indices (as defined by section) of each dof associated with point
6641 
6642  Notes:
6643  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
6644  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
6645  in the local vector.
6646 
6647  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
6648  significant).  It is invalid to call with a global section and setBC=true.
6649 
6650  Developer Note:
6651  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
6652  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
6653  offset could be obtained from the section instead of passing it explicitly as we do now.
6654 
6655  Example:
6656  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
6657  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
6658  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
6659  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.
6660 
6661  Level: developer
6662 */
6663 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[]) {
6664   PetscInt numFields, foff, f;
6665 
6666   PetscFunctionBegin;
6667   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6668   PetscCall(PetscSectionGetNumFields(section, &numFields));
6669   for (f = 0, foff = 0; f < numFields; ++f) {
6670     PetscInt        fdof, cfdof;
6671     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6672     PetscInt        cind = 0, b;
6673     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6674 
6675     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6676     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6677     if (!cfdof || setBC) {
6678       for (b = 0; b < fdof; ++b) {
6679         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6680         const PetscInt ind    = indperm ? indperm[preind] : preind;
6681 
6682         indices[ind] = off + foff + b;
6683       }
6684     } else {
6685       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6686       for (b = 0; b < fdof; ++b) {
6687         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6688         const PetscInt ind    = indperm ? indperm[preind] : preind;
6689 
6690         if ((cind < cfdof) && (b == fcdofs[cind])) {
6691           indices[ind] = -(off + foff + b + 1);
6692           ++cind;
6693         } else {
6694           indices[ind] = off + foff + b - (islocal ? 0 : cind);
6695         }
6696       }
6697     }
6698     foff += (setBC || islocal ? fdof : (fdof - cfdof));
6699     foffs[f] += fdof;
6700   }
6701   PetscFunctionReturn(0);
6702 }
6703 
6704 /*
6705   This version believes the globalSection offsets for each field, rather than just the point offset
6706 
6707  . foffs - The offset into 'indices' for each field, since it is segregated by field
6708 
6709  Notes:
6710  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
6711  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
6712 */
6713 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[]) {
6714   PetscInt numFields, foff, f;
6715 
6716   PetscFunctionBegin;
6717   PetscCall(PetscSectionGetNumFields(section, &numFields));
6718   for (f = 0; f < numFields; ++f) {
6719     PetscInt        fdof, cfdof;
6720     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6721     PetscInt        cind = 0, b;
6722     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
6723 
6724     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6725     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
6726     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
6727     if (!cfdof) {
6728       for (b = 0; b < fdof; ++b) {
6729         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6730         const PetscInt ind    = indperm ? indperm[preind] : preind;
6731 
6732         indices[ind] = foff + b;
6733       }
6734     } else {
6735       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6736       for (b = 0; b < fdof; ++b) {
6737         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
6738         const PetscInt ind    = indperm ? indperm[preind] : preind;
6739 
6740         if ((cind < cfdof) && (b == fcdofs[cind])) {
6741           indices[ind] = -(foff + b + 1);
6742           ++cind;
6743         } else {
6744           indices[ind] = foff + b - cind;
6745         }
6746       }
6747     }
6748     foffs[f] += fdof;
6749   }
6750   PetscFunctionReturn(0);
6751 }
6752 
6753 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) {
6754   Mat             cMat;
6755   PetscSection    aSec, cSec;
6756   IS              aIS;
6757   PetscInt        aStart = -1, aEnd = -1;
6758   const PetscInt *anchors;
6759   PetscInt        numFields, f, p, q, newP = 0;
6760   PetscInt        newNumPoints = 0, newNumIndices = 0;
6761   PetscInt       *newPoints, *indices, *newIndices;
6762   PetscInt        maxAnchor, maxDof;
6763   PetscInt        newOffsets[32];
6764   PetscInt       *pointMatOffsets[32];
6765   PetscInt       *newPointOffsets[32];
6766   PetscScalar    *pointMat[32];
6767   PetscScalar    *newValues      = NULL, *tmpValues;
6768   PetscBool       anyConstrained = PETSC_FALSE;
6769 
6770   PetscFunctionBegin;
6771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6772   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6773   PetscCall(PetscSectionGetNumFields(section, &numFields));
6774 
6775   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
6776   /* if there are point-to-point constraints */
6777   if (aSec) {
6778     PetscCall(PetscArrayzero(newOffsets, 32));
6779     PetscCall(ISGetIndices(aIS, &anchors));
6780     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
6781     /* figure out how many points are going to be in the new element matrix
6782      * (we allow double counting, because it's all just going to be summed
6783      * into the global matrix anyway) */
6784     for (p = 0; p < 2 * numPoints; p += 2) {
6785       PetscInt b    = points[p];
6786       PetscInt bDof = 0, bSecDof;
6787 
6788       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6789       if (!bSecDof) continue;
6790       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6791       if (bDof) {
6792         /* this point is constrained */
6793         /* it is going to be replaced by its anchors */
6794         PetscInt bOff, q;
6795 
6796         anyConstrained = PETSC_TRUE;
6797         newNumPoints += bDof;
6798         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6799         for (q = 0; q < bDof; q++) {
6800           PetscInt a = anchors[bOff + q];
6801           PetscInt aDof;
6802 
6803           PetscCall(PetscSectionGetDof(section, a, &aDof));
6804           newNumIndices += aDof;
6805           for (f = 0; f < numFields; ++f) {
6806             PetscInt fDof;
6807 
6808             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
6809             newOffsets[f + 1] += fDof;
6810           }
6811         }
6812       } else {
6813         /* this point is not constrained */
6814         newNumPoints++;
6815         newNumIndices += bSecDof;
6816         for (f = 0; f < numFields; ++f) {
6817           PetscInt fDof;
6818 
6819           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6820           newOffsets[f + 1] += fDof;
6821         }
6822       }
6823     }
6824   }
6825   if (!anyConstrained) {
6826     if (outNumPoints) *outNumPoints = 0;
6827     if (outNumIndices) *outNumIndices = 0;
6828     if (outPoints) *outPoints = NULL;
6829     if (outValues) *outValues = NULL;
6830     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
6831     PetscFunctionReturn(0);
6832   }
6833 
6834   if (outNumPoints) *outNumPoints = newNumPoints;
6835   if (outNumIndices) *outNumIndices = newNumIndices;
6836 
6837   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
6838 
6839   if (!outPoints && !outValues) {
6840     if (offsets) {
6841       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
6842     }
6843     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
6844     PetscFunctionReturn(0);
6845   }
6846 
6847   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
6848 
6849   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
6850 
6851   /* workspaces */
6852   if (numFields) {
6853     for (f = 0; f < numFields; f++) {
6854       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
6855       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
6856     }
6857   } else {
6858     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
6859     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
6860   }
6861 
6862   /* get workspaces for the point-to-point matrices */
6863   if (numFields) {
6864     PetscInt totalOffset, totalMatOffset;
6865 
6866     for (p = 0; p < numPoints; p++) {
6867       PetscInt b    = points[2 * p];
6868       PetscInt bDof = 0, bSecDof;
6869 
6870       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6871       if (!bSecDof) {
6872         for (f = 0; f < numFields; f++) {
6873           newPointOffsets[f][p + 1] = 0;
6874           pointMatOffsets[f][p + 1] = 0;
6875         }
6876         continue;
6877       }
6878       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6879       if (bDof) {
6880         for (f = 0; f < numFields; f++) {
6881           PetscInt fDof, q, bOff, allFDof = 0;
6882 
6883           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6884           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6885           for (q = 0; q < bDof; q++) {
6886             PetscInt a = anchors[bOff + q];
6887             PetscInt aFDof;
6888 
6889             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
6890             allFDof += aFDof;
6891           }
6892           newPointOffsets[f][p + 1] = allFDof;
6893           pointMatOffsets[f][p + 1] = fDof * allFDof;
6894         }
6895       } else {
6896         for (f = 0; f < numFields; f++) {
6897           PetscInt fDof;
6898 
6899           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
6900           newPointOffsets[f][p + 1] = fDof;
6901           pointMatOffsets[f][p + 1] = 0;
6902         }
6903       }
6904     }
6905     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
6906       newPointOffsets[f][0] = totalOffset;
6907       pointMatOffsets[f][0] = totalMatOffset;
6908       for (p = 0; p < numPoints; p++) {
6909         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
6910         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
6911       }
6912       totalOffset    = newPointOffsets[f][numPoints];
6913       totalMatOffset = pointMatOffsets[f][numPoints];
6914       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
6915     }
6916   } else {
6917     for (p = 0; p < numPoints; p++) {
6918       PetscInt b    = points[2 * p];
6919       PetscInt bDof = 0, bSecDof;
6920 
6921       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6922       if (!bSecDof) {
6923         newPointOffsets[0][p + 1] = 0;
6924         pointMatOffsets[0][p + 1] = 0;
6925         continue;
6926       }
6927       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6928       if (bDof) {
6929         PetscInt bOff, q, allDof = 0;
6930 
6931         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6932         for (q = 0; q < bDof; q++) {
6933           PetscInt a = anchors[bOff + q], aDof;
6934 
6935           PetscCall(PetscSectionGetDof(section, a, &aDof));
6936           allDof += aDof;
6937         }
6938         newPointOffsets[0][p + 1] = allDof;
6939         pointMatOffsets[0][p + 1] = bSecDof * allDof;
6940       } else {
6941         newPointOffsets[0][p + 1] = bSecDof;
6942         pointMatOffsets[0][p + 1] = 0;
6943       }
6944     }
6945     newPointOffsets[0][0] = 0;
6946     pointMatOffsets[0][0] = 0;
6947     for (p = 0; p < numPoints; p++) {
6948       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
6949       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
6950     }
6951     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
6952   }
6953 
6954   /* output arrays */
6955   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
6956 
6957   /* get the point-to-point matrices; construct newPoints */
6958   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
6959   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
6960   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
6961   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
6962   if (numFields) {
6963     for (p = 0, newP = 0; p < numPoints; p++) {
6964       PetscInt b    = points[2 * p];
6965       PetscInt o    = points[2 * p + 1];
6966       PetscInt bDof = 0, bSecDof;
6967 
6968       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
6969       if (!bSecDof) continue;
6970       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
6971       if (bDof) {
6972         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
6973 
6974         fStart[0] = 0;
6975         fEnd[0]   = 0;
6976         for (f = 0; f < numFields; f++) {
6977           PetscInt fDof;
6978 
6979           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
6980           fStart[f + 1] = fStart[f] + fDof;
6981           fEnd[f + 1]   = fStart[f + 1];
6982         }
6983         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
6984         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
6985 
6986         fAnchorStart[0] = 0;
6987         fAnchorEnd[0]   = 0;
6988         for (f = 0; f < numFields; f++) {
6989           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
6990 
6991           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
6992           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
6993         }
6994         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
6995         for (q = 0; q < bDof; q++) {
6996           PetscInt a = anchors[bOff + q], aOff;
6997 
6998           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
6999           newPoints[2 * (newP + q)]     = a;
7000           newPoints[2 * (newP + q) + 1] = 0;
7001           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7002           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7003         }
7004         newP += bDof;
7005 
7006         if (outValues) {
7007           /* get the point-to-point submatrix */
7008           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]));
7009         }
7010       } else {
7011         newPoints[2 * newP]     = b;
7012         newPoints[2 * newP + 1] = o;
7013         newP++;
7014       }
7015     }
7016   } else {
7017     for (p = 0; p < numPoints; p++) {
7018       PetscInt b    = points[2 * p];
7019       PetscInt o    = points[2 * p + 1];
7020       PetscInt bDof = 0, bSecDof;
7021 
7022       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7023       if (!bSecDof) continue;
7024       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7025       if (bDof) {
7026         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7027 
7028         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7029         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7030 
7031         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7032         for (q = 0; q < bDof; q++) {
7033           PetscInt a = anchors[bOff + q], aOff;
7034 
7035           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7036 
7037           newPoints[2 * (newP + q)]     = a;
7038           newPoints[2 * (newP + q) + 1] = 0;
7039           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7040           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7041         }
7042         newP += bDof;
7043 
7044         /* get the point-to-point submatrix */
7045         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7046       } else {
7047         newPoints[2 * newP]     = b;
7048         newPoints[2 * newP + 1] = o;
7049         newP++;
7050       }
7051     }
7052   }
7053 
7054   if (outValues) {
7055     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7056     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7057     /* multiply constraints on the right */
7058     if (numFields) {
7059       for (f = 0; f < numFields; f++) {
7060         PetscInt oldOff = offsets[f];
7061 
7062         for (p = 0; p < numPoints; p++) {
7063           PetscInt cStart = newPointOffsets[f][p];
7064           PetscInt b      = points[2 * p];
7065           PetscInt c, r, k;
7066           PetscInt dof;
7067 
7068           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7069           if (!dof) continue;
7070           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7071             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7072             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7073 
7074             for (r = 0; r < numIndices; r++) {
7075               for (c = 0; c < nCols; c++) {
7076                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7077               }
7078             }
7079           } else {
7080             /* copy this column as is */
7081             for (r = 0; r < numIndices; r++) {
7082               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7083             }
7084           }
7085           oldOff += dof;
7086         }
7087       }
7088     } else {
7089       PetscInt oldOff = 0;
7090       for (p = 0; p < numPoints; p++) {
7091         PetscInt cStart = newPointOffsets[0][p];
7092         PetscInt b      = points[2 * p];
7093         PetscInt c, r, k;
7094         PetscInt dof;
7095 
7096         PetscCall(PetscSectionGetDof(section, b, &dof));
7097         if (!dof) continue;
7098         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7099           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7100           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7101 
7102           for (r = 0; r < numIndices; r++) {
7103             for (c = 0; c < nCols; c++) {
7104               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7105             }
7106           }
7107         } else {
7108           /* copy this column as is */
7109           for (r = 0; r < numIndices; r++) {
7110             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7111           }
7112         }
7113         oldOff += dof;
7114       }
7115     }
7116 
7117     if (multiplyLeft) {
7118       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7119       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7120       /* multiply constraints transpose on the left */
7121       if (numFields) {
7122         for (f = 0; f < numFields; f++) {
7123           PetscInt oldOff = offsets[f];
7124 
7125           for (p = 0; p < numPoints; p++) {
7126             PetscInt rStart = newPointOffsets[f][p];
7127             PetscInt b      = points[2 * p];
7128             PetscInt c, r, k;
7129             PetscInt dof;
7130 
7131             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7132             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7133               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7134               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7135 
7136               for (r = 0; r < nRows; r++) {
7137                 for (c = 0; c < newNumIndices; c++) {
7138                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7139                 }
7140               }
7141             } else {
7142               /* copy this row as is */
7143               for (r = 0; r < dof; r++) {
7144                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7145               }
7146             }
7147             oldOff += dof;
7148           }
7149         }
7150       } else {
7151         PetscInt oldOff = 0;
7152 
7153         for (p = 0; p < numPoints; p++) {
7154           PetscInt rStart = newPointOffsets[0][p];
7155           PetscInt b      = points[2 * p];
7156           PetscInt c, r, k;
7157           PetscInt dof;
7158 
7159           PetscCall(PetscSectionGetDof(section, b, &dof));
7160           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7161             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7162             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7163 
7164             for (r = 0; r < nRows; r++) {
7165               for (c = 0; c < newNumIndices; c++) {
7166                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7167               }
7168             }
7169           } else {
7170             /* copy this row as is */
7171             for (r = 0; r < dof; r++) {
7172               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7173             }
7174           }
7175           oldOff += dof;
7176         }
7177       }
7178 
7179       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7180     } else {
7181       newValues = tmpValues;
7182     }
7183   }
7184 
7185   /* clean up */
7186   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7187   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7188 
7189   if (numFields) {
7190     for (f = 0; f < numFields; f++) {
7191       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7192       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7193       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7194     }
7195   } else {
7196     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7197     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7198     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7199   }
7200   PetscCall(ISRestoreIndices(aIS, &anchors));
7201 
7202   /* output */
7203   if (outPoints) {
7204     *outPoints = newPoints;
7205   } else {
7206     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7207   }
7208   if (outValues) *outValues = newValues;
7209   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7210   PetscFunctionReturn(0);
7211 }
7212 
7213 /*@C
7214   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7215 
7216   Not collective
7217 
7218   Input Parameters:
7219 + dm         - The DM
7220 . section    - The PetscSection describing the points (a local section)
7221 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7222 . point      - The point defining the closure
7223 - useClPerm  - Use the closure point permutation if available
7224 
7225   Output Parameters:
7226 + numIndices - The number of dof indices in the closure of point with the input sections
7227 . indices    - The dof indices
7228 . outOffsets - Array to write the field offsets into, or NULL
7229 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7230 
7231   Notes:
7232   Must call DMPlexRestoreClosureIndices() to free allocated memory
7233 
7234   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7235   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7236   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7237   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7238   indices (with the above semantics) are implied.
7239 
7240   Level: advanced
7241 
7242 .seealso `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7243 @*/
7244 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[]) {
7245   /* Closure ordering */
7246   PetscSection        clSection;
7247   IS                  clPoints;
7248   const PetscInt     *clp;
7249   PetscInt           *points;
7250   const PetscInt     *clperm    = NULL;
7251   /* Dof permutation and sign flips */
7252   const PetscInt    **perms[32] = {NULL};
7253   const PetscScalar **flips[32] = {NULL};
7254   PetscScalar        *valCopy   = NULL;
7255   /* Hanging node constraints */
7256   PetscInt           *pointsC   = NULL;
7257   PetscScalar        *valuesC   = NULL;
7258   PetscInt            NclC, NiC;
7259 
7260   PetscInt *idx;
7261   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7262   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7263 
7264   PetscFunctionBeginHot;
7265   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7266   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7267   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7268   if (numIndices) PetscValidIntPointer(numIndices, 6);
7269   if (indices) PetscValidPointer(indices, 7);
7270   if (outOffsets) PetscValidIntPointer(outOffsets, 8);
7271   if (values) PetscValidPointer(values, 9);
7272   PetscCall(PetscSectionGetNumFields(section, &Nf));
7273   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7274   PetscCall(PetscArrayzero(offsets, 32));
7275   /* 1) Get points in closure */
7276   PetscCall(DMPlexGetCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7277   if (useClPerm) {
7278     PetscInt depth, clsize;
7279     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7280     for (clsize = 0, p = 0; p < Ncl; p++) {
7281       PetscInt dof;
7282       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7283       clsize += dof;
7284     }
7285     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7286   }
7287   /* 2) Get number of indices on these points and field offsets from section */
7288   for (p = 0; p < Ncl * 2; p += 2) {
7289     PetscInt dof, fdof;
7290 
7291     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7292     for (f = 0; f < Nf; ++f) {
7293       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7294       offsets[f + 1] += fdof;
7295     }
7296     Ni += dof;
7297   }
7298   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7299   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7300   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7301   for (f = 0; f < PetscMax(1, Nf); ++f) {
7302     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7303     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7304     /* may need to apply sign changes to the element matrix */
7305     if (values && flips[f]) {
7306       PetscInt foffset = offsets[f];
7307 
7308       for (p = 0; p < Ncl; ++p) {
7309         PetscInt           pnt  = points[2 * p], fdof;
7310         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7311 
7312         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7313         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7314         if (flip) {
7315           PetscInt i, j, k;
7316 
7317           if (!valCopy) {
7318             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7319             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7320             *values = valCopy;
7321           }
7322           for (i = 0; i < fdof; ++i) {
7323             PetscScalar fval = flip[i];
7324 
7325             for (k = 0; k < Ni; ++k) {
7326               valCopy[Ni * (foffset + i) + k] *= fval;
7327               valCopy[Ni * k + (foffset + i)] *= fval;
7328             }
7329           }
7330         }
7331         foffset += fdof;
7332       }
7333     }
7334   }
7335   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7336   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7337   if (NclC) {
7338     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7339     for (f = 0; f < PetscMax(1, Nf); ++f) {
7340       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7341       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7342     }
7343     for (f = 0; f < PetscMax(1, Nf); ++f) {
7344       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7345       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7346     }
7347     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7348     Ncl    = NclC;
7349     Ni     = NiC;
7350     points = pointsC;
7351     if (values) *values = valuesC;
7352   }
7353   /* 5) Calculate indices */
7354   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7355   if (Nf) {
7356     PetscInt  idxOff;
7357     PetscBool useFieldOffsets;
7358 
7359     if (outOffsets) {
7360       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7361     }
7362     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7363     if (useFieldOffsets) {
7364       for (p = 0; p < Ncl; ++p) {
7365         const PetscInt pnt = points[p * 2];
7366 
7367         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7368       }
7369     } else {
7370       for (p = 0; p < Ncl; ++p) {
7371         const PetscInt pnt = points[p * 2];
7372 
7373         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7374         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7375          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7376          * global section. */
7377         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7378       }
7379     }
7380   } else {
7381     PetscInt off = 0, idxOff;
7382 
7383     for (p = 0; p < Ncl; ++p) {
7384       const PetscInt  pnt  = points[p * 2];
7385       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7386 
7387       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7388       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7389        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7390       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7391     }
7392   }
7393   /* 6) Cleanup */
7394   for (f = 0; f < PetscMax(1, Nf); ++f) {
7395     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7396     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7397   }
7398   if (NclC) {
7399     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7400   } else {
7401     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7402   }
7403 
7404   if (numIndices) *numIndices = Ni;
7405   if (indices) *indices = idx;
7406   PetscFunctionReturn(0);
7407 }
7408 
7409 /*@C
7410   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7411 
7412   Not collective
7413 
7414   Input Parameters:
7415 + dm         - The DM
7416 . section    - The PetscSection describing the points (a local section)
7417 . idxSection - The PetscSection from which to obtain indices (may be local or global)
7418 . point      - The point defining the closure
7419 - useClPerm  - Use the closure point permutation if available
7420 
7421   Output Parameters:
7422 + numIndices - The number of dof indices in the closure of point with the input sections
7423 . indices    - The dof indices
7424 . outOffsets - Array to write the field offsets into, or NULL
7425 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or NULL
7426 
7427   Notes:
7428   If values were modified, the user is responsible for calling DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values).
7429 
7430   If idxSection is global, any constrained dofs (see DMAddBoundary(), for example) will get negative indices.  The value
7431   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7432   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7433   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7434   indices (with the above semantics) are implied.
7435 
7436   Level: advanced
7437 
7438 .seealso `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7439 @*/
7440 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[]) {
7441   PetscFunctionBegin;
7442   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7443   PetscValidPointer(indices, 7);
7444   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7445   PetscFunctionReturn(0);
7446 }
7447 
7448 /*@C
7449   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7450 
7451   Not collective
7452 
7453   Input Parameters:
7454 + dm - The DM
7455 . section - The section describing the layout in v, or NULL to use the default section
7456 . globalSection - The section describing the layout in v, or NULL to use the default global section
7457 . A - The matrix
7458 . point - The point in the DM
7459 . values - The array of values
7460 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7461 
7462   Fortran Notes:
7463   This routine is only available in Fortran 90, and you must include petsc.h90 in your code.
7464 
7465   Level: intermediate
7466 
7467 .seealso `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7468 @*/
7469 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode) {
7470   DM_Plex           *mesh = (DM_Plex *)dm->data;
7471   PetscInt          *indices;
7472   PetscInt           numIndices;
7473   const PetscScalar *valuesOrig = values;
7474   PetscErrorCode     ierr;
7475 
7476   PetscFunctionBegin;
7477   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7478   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7479   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7480   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7481   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7482   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7483 
7484   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7485 
7486   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7487   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7488   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7489   if (ierr) {
7490     PetscMPIInt rank;
7491 
7492     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7493     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7494     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7495     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7496     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7497     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7498   }
7499   if (mesh->printFEM > 1) {
7500     PetscInt i;
7501     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7502     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7503     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7504   }
7505 
7506   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7507   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7508   PetscFunctionReturn(0);
7509 }
7510 
7511 /*@C
7512   DMPlexMatSetClosure - Set an array of the values on the closure of 'point' using a different row and column section
7513 
7514   Not collective
7515 
7516   Input Parameters:
7517 + dmRow - The DM for the row fields
7518 . sectionRow - The section describing the layout, or NULL to use the default section in dmRow
7519 . globalSectionRow - The section describing the layout, or NULL to use the default global section in dmRow
7520 . dmCol - The DM for the column fields
7521 . sectionCol - The section describing the layout, or NULL to use the default section in dmCol
7522 . globalSectionCol - The section describing the layout, or NULL to use the default global section in dmCol
7523 . A - The matrix
7524 . point - The point in the DMs
7525 . values - The array of values
7526 - mode - The insert mode, where INSERT_ALL_VALUES and ADD_ALL_VALUES also overwrite boundary conditions
7527 
7528   Level: intermediate
7529 
7530 .seealso `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7531 @*/
7532 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode) {
7533   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7534   PetscInt          *indicesRow, *indicesCol;
7535   PetscInt           numIndicesRow, numIndicesCol;
7536   const PetscScalar *valuesOrig = values;
7537   PetscErrorCode     ierr;
7538 
7539   PetscFunctionBegin;
7540   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7541   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7542   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7543   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7544   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7545   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7546   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7547   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7548   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7549   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7550   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7551 
7552   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7553   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7554 
7555   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7556   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7557   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7558   if (ierr) {
7559     PetscMPIInt rank;
7560 
7561     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7562     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7563     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7564     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7565     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7566     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7567   }
7568 
7569   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7570   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7571   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7572   PetscFunctionReturn(0);
7573 }
7574 
7575 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode) {
7576   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7577   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7578   PetscInt       *cpoints = NULL;
7579   PetscInt       *findices, *cindices;
7580   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7581   PetscInt        foffsets[32], coffsets[32];
7582   DMPolytopeType  ct;
7583   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7584   PetscErrorCode  ierr;
7585 
7586   PetscFunctionBegin;
7587   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7588   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7589   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7590   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7591   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7592   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7593   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7594   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7595   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7596   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7597   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7598   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7599   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7600   PetscCall(PetscArrayzero(foffsets, 32));
7601   PetscCall(PetscArrayzero(coffsets, 32));
7602   /* Column indices */
7603   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7604   maxFPoints = numCPoints;
7605   /* Compress out points not in the section */
7606   /*   TODO: Squeeze out points with 0 dof as well */
7607   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7608   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7609     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7610       cpoints[q * 2]     = cpoints[p];
7611       cpoints[q * 2 + 1] = cpoints[p + 1];
7612       ++q;
7613     }
7614   }
7615   numCPoints = q;
7616   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7617     PetscInt fdof;
7618 
7619     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7620     if (!dof) continue;
7621     for (f = 0; f < numFields; ++f) {
7622       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7623       coffsets[f + 1] += fdof;
7624     }
7625     numCIndices += dof;
7626   }
7627   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7628   /* Row indices */
7629   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7630   {
7631     DMPlexTransform tr;
7632     DMPolytopeType *rct;
7633     PetscInt       *rsize, *rcone, *rornt, Nt;
7634 
7635     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7636     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7637     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7638     numSubcells = rsize[Nt - 1];
7639     PetscCall(DMPlexTransformDestroy(&tr));
7640   }
7641   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7642   for (r = 0, q = 0; r < numSubcells; ++r) {
7643     /* TODO Map from coarse to fine cells */
7644     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7645     /* Compress out points not in the section */
7646     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7647     for (p = 0; p < numFPoints * 2; p += 2) {
7648       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7649         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7650         if (!dof) continue;
7651         for (s = 0; s < q; ++s)
7652           if (fpoints[p] == ftotpoints[s * 2]) break;
7653         if (s < q) continue;
7654         ftotpoints[q * 2]     = fpoints[p];
7655         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7656         ++q;
7657       }
7658     }
7659     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7660   }
7661   numFPoints = q;
7662   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7663     PetscInt fdof;
7664 
7665     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7666     if (!dof) continue;
7667     for (f = 0; f < numFields; ++f) {
7668       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7669       foffsets[f + 1] += fdof;
7670     }
7671     numFIndices += dof;
7672   }
7673   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7674 
7675   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7676   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7677   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7678   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7679   if (numFields) {
7680     const PetscInt **permsF[32] = {NULL};
7681     const PetscInt **permsC[32] = {NULL};
7682 
7683     for (f = 0; f < numFields; f++) {
7684       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7685       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7686     }
7687     for (p = 0; p < numFPoints; p++) {
7688       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7689       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7690     }
7691     for (p = 0; p < numCPoints; p++) {
7692       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7693       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7694     }
7695     for (f = 0; f < numFields; f++) {
7696       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7697       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7698     }
7699   } else {
7700     const PetscInt **permsF = NULL;
7701     const PetscInt **permsC = NULL;
7702 
7703     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7704     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7705     for (p = 0, off = 0; p < numFPoints; p++) {
7706       const PetscInt *perm = permsF ? permsF[p] : NULL;
7707 
7708       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7709       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7710     }
7711     for (p = 0, off = 0; p < numCPoints; p++) {
7712       const PetscInt *perm = permsC ? permsC[p] : NULL;
7713 
7714       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7715       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7716     }
7717     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7718     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7719   }
7720   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7721   /* TODO: flips */
7722   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7723   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
7724   if (ierr) {
7725     PetscMPIInt rank;
7726 
7727     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7728     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7729     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
7730     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7731     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7732   }
7733   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
7734   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7735   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
7736   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
7737   PetscFunctionReturn(0);
7738 }
7739 
7740 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[]) {
7741   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7742   PetscInt       *cpoints = NULL;
7743   PetscInt        foffsets[32], coffsets[32];
7744   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7745   DMPolytopeType  ct;
7746   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7747 
7748   PetscFunctionBegin;
7749   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7750   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7751   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7752   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7753   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7754   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7755   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7756   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7757   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7758   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7759   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7760   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7761   PetscCall(PetscArrayzero(foffsets, 32));
7762   PetscCall(PetscArrayzero(coffsets, 32));
7763   /* Column indices */
7764   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7765   maxFPoints = numCPoints;
7766   /* Compress out points not in the section */
7767   /*   TODO: Squeeze out points with 0 dof as well */
7768   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7769   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7770     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7771       cpoints[q * 2]     = cpoints[p];
7772       cpoints[q * 2 + 1] = cpoints[p + 1];
7773       ++q;
7774     }
7775   }
7776   numCPoints = q;
7777   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
7778     PetscInt fdof;
7779 
7780     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
7781     if (!dof) continue;
7782     for (f = 0; f < numFields; ++f) {
7783       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
7784       coffsets[f + 1] += fdof;
7785     }
7786     numCIndices += dof;
7787   }
7788   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
7789   /* Row indices */
7790   PetscCall(DMPlexGetCellType(dmc, point, &ct));
7791   {
7792     DMPlexTransform tr;
7793     DMPolytopeType *rct;
7794     PetscInt       *rsize, *rcone, *rornt, Nt;
7795 
7796     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
7797     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
7798     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
7799     numSubcells = rsize[Nt - 1];
7800     PetscCall(DMPlexTransformDestroy(&tr));
7801   }
7802   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
7803   for (r = 0, q = 0; r < numSubcells; ++r) {
7804     /* TODO Map from coarse to fine cells */
7805     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
7806     /* Compress out points not in the section */
7807     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
7808     for (p = 0; p < numFPoints * 2; p += 2) {
7809       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
7810         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
7811         if (!dof) continue;
7812         for (s = 0; s < q; ++s)
7813           if (fpoints[p] == ftotpoints[s * 2]) break;
7814         if (s < q) continue;
7815         ftotpoints[q * 2]     = fpoints[p];
7816         ftotpoints[q * 2 + 1] = fpoints[p + 1];
7817         ++q;
7818       }
7819     }
7820     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
7821   }
7822   numFPoints = q;
7823   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
7824     PetscInt fdof;
7825 
7826     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
7827     if (!dof) continue;
7828     for (f = 0; f < numFields; ++f) {
7829       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
7830       foffsets[f + 1] += fdof;
7831     }
7832     numFIndices += dof;
7833   }
7834   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
7835 
7836   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
7837   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
7838   if (numFields) {
7839     const PetscInt **permsF[32] = {NULL};
7840     const PetscInt **permsC[32] = {NULL};
7841 
7842     for (f = 0; f < numFields; f++) {
7843       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7844       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7845     }
7846     for (p = 0; p < numFPoints; p++) {
7847       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7848       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
7849     }
7850     for (p = 0; p < numCPoints; p++) {
7851       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7852       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
7853     }
7854     for (f = 0; f < numFields; f++) {
7855       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
7856       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
7857     }
7858   } else {
7859     const PetscInt **permsF = NULL;
7860     const PetscInt **permsC = NULL;
7861 
7862     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7863     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7864     for (p = 0, off = 0; p < numFPoints; p++) {
7865       const PetscInt *perm = permsF ? permsF[p] : NULL;
7866 
7867       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
7868       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
7869     }
7870     for (p = 0, off = 0; p < numCPoints; p++) {
7871       const PetscInt *perm = permsC ? permsC[p] : NULL;
7872 
7873       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
7874       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
7875     }
7876     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
7877     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
7878   }
7879   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
7880   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7881   PetscFunctionReturn(0);
7882 }
7883 
7884 /*@C
7885   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
7886 
7887   Input Parameter:
7888 . dm   - The DMPlex object
7889 
7890   Output Parameter:
7891 . cellHeight - The height of a cell
7892 
7893   Level: developer
7894 
7895 .seealso `DMPlexSetVTKCellHeight()`
7896 @*/
7897 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight) {
7898   DM_Plex *mesh = (DM_Plex *)dm->data;
7899 
7900   PetscFunctionBegin;
7901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7902   PetscValidIntPointer(cellHeight, 2);
7903   *cellHeight = mesh->vtkCellHeight;
7904   PetscFunctionReturn(0);
7905 }
7906 
7907 /*@C
7908   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
7909 
7910   Input Parameters:
7911 + dm   - The DMPlex object
7912 - cellHeight - The height of a cell
7913 
7914   Level: developer
7915 
7916 .seealso `DMPlexGetVTKCellHeight()`
7917 @*/
7918 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight) {
7919   DM_Plex *mesh = (DM_Plex *)dm->data;
7920 
7921   PetscFunctionBegin;
7922   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7923   mesh->vtkCellHeight = cellHeight;
7924   PetscFunctionReturn(0);
7925 }
7926 
7927 /*@
7928   DMPlexGetGhostCellStratum - Get the range of cells which are used to enforce FV boundary conditions
7929 
7930   Input Parameter:
7931 . dm - The DMPlex object
7932 
7933   Output Parameters:
7934 + gcStart - The first ghost cell, or NULL
7935 - gcEnd   - The upper bound on ghost cells, or NULL
7936 
7937   Level: advanced
7938 
7939 .seealso `DMPlexConstructGhostCells()`, `DMPlexGetGhostCellStratum()`
7940 @*/
7941 PetscErrorCode DMPlexGetGhostCellStratum(DM dm, PetscInt *gcStart, PetscInt *gcEnd) {
7942   DMLabel ctLabel;
7943 
7944   PetscFunctionBegin;
7945   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7946   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
7947   PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_FV_GHOST, gcStart, gcEnd));
7948   // Reset label for fast lookup
7949   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
7950   PetscFunctionReturn(0);
7951 }
7952 
7953 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering) {
7954   PetscSection section, globalSection;
7955   PetscInt    *numbers, p;
7956 
7957   PetscFunctionBegin;
7958   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
7959   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
7960   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
7961   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
7962   PetscCall(PetscSectionSetUp(section));
7963   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
7964   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
7965   for (p = pStart; p < pEnd; ++p) {
7966     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
7967     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
7968     else numbers[p - pStart] += shift;
7969   }
7970   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
7971   if (globalSize) {
7972     PetscLayout layout;
7973     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
7974     PetscCall(PetscLayoutGetSize(layout, globalSize));
7975     PetscCall(PetscLayoutDestroy(&layout));
7976   }
7977   PetscCall(PetscSectionDestroy(&section));
7978   PetscCall(PetscSectionDestroy(&globalSection));
7979   PetscFunctionReturn(0);
7980 }
7981 
7982 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers) {
7983   PetscInt cellHeight, cStart, cEnd;
7984 
7985   PetscFunctionBegin;
7986   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
7987   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
7988   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
7989   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
7990   PetscFunctionReturn(0);
7991 }
7992 
7993 /*@
7994   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
7995 
7996   Input Parameter:
7997 . dm   - The DMPlex object
7998 
7999   Output Parameter:
8000 . globalCellNumbers - Global cell numbers for all cells on this process
8001 
8002   Level: developer
8003 
8004 .seealso `DMPlexGetVertexNumbering()`
8005 @*/
8006 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers) {
8007   DM_Plex *mesh = (DM_Plex *)dm->data;
8008 
8009   PetscFunctionBegin;
8010   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8011   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8012   *globalCellNumbers = mesh->globalCellNumbers;
8013   PetscFunctionReturn(0);
8014 }
8015 
8016 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers) {
8017   PetscInt vStart, vEnd;
8018 
8019   PetscFunctionBegin;
8020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8021   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8022   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8023   PetscFunctionReturn(0);
8024 }
8025 
8026 /*@
8027   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8028 
8029   Input Parameter:
8030 . dm   - The DMPlex object
8031 
8032   Output Parameter:
8033 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8034 
8035   Level: developer
8036 
8037 .seealso `DMPlexGetCellNumbering()`
8038 @*/
8039 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers) {
8040   DM_Plex *mesh = (DM_Plex *)dm->data;
8041 
8042   PetscFunctionBegin;
8043   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8044   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8045   *globalVertexNumbers = mesh->globalVertexNumbers;
8046   PetscFunctionReturn(0);
8047 }
8048 
8049 /*@
8050   DMPlexCreatePointNumbering - Create a global numbering for all points.
8051 
8052   Collective on dm
8053 
8054   Input Parameter:
8055 . dm   - The DMPlex object
8056 
8057   Output Parameter:
8058 . globalPointNumbers - Global numbers for all points on this process
8059 
8060   Notes:
8061 
8062   The point numbering IS is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8063   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8064   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8065   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8066 
8067   The partitioned mesh is
8068 ```
8069  (2)--0--(3)--1--(4)    (1)--0--(2)
8070 ```
8071   and its global numbering is
8072 ```
8073   (3)--0--(4)--1--(5)--2--(6)
8074 ```
8075   Then the global numbering is provided as
8076 ```
8077 [0] Number of indices in set 5
8078 [0] 0 0
8079 [0] 1 1
8080 [0] 2 3
8081 [0] 3 4
8082 [0] 4 -6
8083 [1] Number of indices in set 3
8084 [1] 0 2
8085 [1] 1 5
8086 [1] 2 6
8087 ```
8088 
8089   Level: developer
8090 
8091 .seealso `DMPlexGetCellNumbering()`
8092 @*/
8093 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers) {
8094   IS        nums[4];
8095   PetscInt  depths[4], gdepths[4], starts[4];
8096   PetscInt  depth, d, shift = 0;
8097   PetscBool empty = PETSC_FALSE;
8098 
8099   PetscFunctionBegin;
8100   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8101   PetscCall(DMPlexGetDepth(dm, &depth));
8102   // For unstratified meshes use dim instead of depth
8103   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8104   // If any stratum is empty, we must mark all empty
8105   for (d = 0; d <= depth; ++d) {
8106     PetscInt end;
8107 
8108     depths[d] = depth - d;
8109     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8110     if (!(starts[d] - end)) empty = PETSC_TRUE;
8111   }
8112   if (empty)
8113     for (d = 0; d <= depth; ++d) {
8114       depths[d] = -1;
8115       starts[d] = -1;
8116     }
8117   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8118   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8119   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]);
8120   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8121   for (d = 0; d <= depth; ++d) {
8122     PetscInt pStart, pEnd, gsize;
8123 
8124     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8125     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8126     shift += gsize;
8127   }
8128   PetscCall(ISConcatenate(PetscObjectComm((PetscObject)dm), depth + 1, nums, globalPointNumbers));
8129   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8130   PetscFunctionReturn(0);
8131 }
8132 
8133 /*@
8134   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8135 
8136   Input Parameter:
8137 . dm - The DMPlex object
8138 
8139   Output Parameter:
8140 . ranks - The rank field
8141 
8142   Options Database Keys:
8143 . -dm_partition_view - Adds the rank field into the DM output from -dm_view using the same viewer
8144 
8145   Level: intermediate
8146 
8147 .seealso: `DMView()`
8148 @*/
8149 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks) {
8150   DM             rdm;
8151   PetscFE        fe;
8152   PetscScalar   *r;
8153   PetscMPIInt    rank;
8154   DMPolytopeType ct;
8155   PetscInt       dim, cStart, cEnd, c;
8156   PetscBool      simplex;
8157 
8158   PetscFunctionBeginUser;
8159   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8160   PetscValidPointer(ranks, 2);
8161   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8162   PetscCall(DMClone(dm, &rdm));
8163   PetscCall(DMGetDimension(rdm, &dim));
8164   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8165   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8166   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8167   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8168   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8169   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8170   PetscCall(PetscFEDestroy(&fe));
8171   PetscCall(DMCreateDS(rdm));
8172   PetscCall(DMCreateGlobalVector(rdm, ranks));
8173   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8174   PetscCall(VecGetArray(*ranks, &r));
8175   for (c = cStart; c < cEnd; ++c) {
8176     PetscScalar *lr;
8177 
8178     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8179     if (lr) *lr = rank;
8180   }
8181   PetscCall(VecRestoreArray(*ranks, &r));
8182   PetscCall(DMDestroy(&rdm));
8183   PetscFunctionReturn(0);
8184 }
8185 
8186 /*@
8187   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8188 
8189   Input Parameters:
8190 + dm    - The DMPlex
8191 - label - The DMLabel
8192 
8193   Output Parameter:
8194 . val - The label value field
8195 
8196   Options Database Keys:
8197 . -dm_label_view - Adds the label value field into the DM output from -dm_view using the same viewer
8198 
8199   Level: intermediate
8200 
8201 .seealso: `DMView()`
8202 @*/
8203 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val) {
8204   DM           rdm;
8205   PetscFE      fe;
8206   PetscScalar *v;
8207   PetscInt     dim, cStart, cEnd, c;
8208 
8209   PetscFunctionBeginUser;
8210   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8211   PetscValidPointer(label, 2);
8212   PetscValidPointer(val, 3);
8213   PetscCall(DMClone(dm, &rdm));
8214   PetscCall(DMGetDimension(rdm, &dim));
8215   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8216   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8217   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8218   PetscCall(PetscFEDestroy(&fe));
8219   PetscCall(DMCreateDS(rdm));
8220   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8221   PetscCall(DMCreateGlobalVector(rdm, val));
8222   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8223   PetscCall(VecGetArray(*val, &v));
8224   for (c = cStart; c < cEnd; ++c) {
8225     PetscScalar *lv;
8226     PetscInt     cval;
8227 
8228     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8229     PetscCall(DMLabelGetValue(label, c, &cval));
8230     *lv = cval;
8231   }
8232   PetscCall(VecRestoreArray(*val, &v));
8233   PetscCall(DMDestroy(&rdm));
8234   PetscFunctionReturn(0);
8235 }
8236 
8237 /*@
8238   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8239 
8240   Input Parameter:
8241 . dm - The DMPlex object
8242 
8243   Notes:
8244   This is a useful diagnostic when creating meshes programmatically.
8245 
8246   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8247 
8248   Level: developer
8249 
8250 .seealso: `DMCreate()`, `DMSetFromOptions()`
8251 @*/
8252 PetscErrorCode DMPlexCheckSymmetry(DM dm) {
8253   PetscSection    coneSection, supportSection;
8254   const PetscInt *cone, *support;
8255   PetscInt        coneSize, c, supportSize, s;
8256   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8257   PetscBool       storagecheck = PETSC_TRUE;
8258 
8259   PetscFunctionBegin;
8260   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8261   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8262   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8263   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8264   /* Check that point p is found in the support of its cone points, and vice versa */
8265   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8266   for (p = pStart; p < pEnd; ++p) {
8267     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8268     PetscCall(DMPlexGetCone(dm, p, &cone));
8269     for (c = 0; c < coneSize; ++c) {
8270       PetscBool dup = PETSC_FALSE;
8271       PetscInt  d;
8272       for (d = c - 1; d >= 0; --d) {
8273         if (cone[c] == cone[d]) {
8274           dup = PETSC_TRUE;
8275           break;
8276         }
8277       }
8278       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8279       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8280       for (s = 0; s < supportSize; ++s) {
8281         if (support[s] == p) break;
8282       }
8283       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8284         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8285         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8286         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8287         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8288         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8289         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8290         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]);
8291         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8292       }
8293     }
8294     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8295     if (p != pp) {
8296       storagecheck = PETSC_FALSE;
8297       continue;
8298     }
8299     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8300     PetscCall(DMPlexGetSupport(dm, p, &support));
8301     for (s = 0; s < supportSize; ++s) {
8302       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8303       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8304       for (c = 0; c < coneSize; ++c) {
8305         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8306         if (cone[c] != pp) {
8307           c = 0;
8308           break;
8309         }
8310         if (cone[c] == p) break;
8311       }
8312       if (c >= coneSize) {
8313         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8314         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8315         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8316         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8317         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8318         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8319         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8320       }
8321     }
8322   }
8323   if (storagecheck) {
8324     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8325     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8326     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8327   }
8328   PetscFunctionReturn(0);
8329 }
8330 
8331 /*
8332   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.
8333 */
8334 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit) {
8335   DMPolytopeType  cct;
8336   PetscInt        ptpoints[4];
8337   const PetscInt *cone, *ccone, *ptcone;
8338   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8339 
8340   PetscFunctionBegin;
8341   *unsplit = 0;
8342   switch (ct) {
8343   case DM_POLYTOPE_POINT_PRISM_TENSOR: ptpoints[npt++] = c; break;
8344   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8345     PetscCall(DMPlexGetCone(dm, c, &cone));
8346     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8347     for (cp = 0; cp < coneSize; ++cp) {
8348       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8349       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8350     }
8351     break;
8352   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8353   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8354     PetscCall(DMPlexGetCone(dm, c, &cone));
8355     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8356     for (cp = 0; cp < coneSize; ++cp) {
8357       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8358       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8359       for (ccp = 0; ccp < cconeSize; ++ccp) {
8360         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8361         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8362           PetscInt p;
8363           for (p = 0; p < npt; ++p)
8364             if (ptpoints[p] == ccone[ccp]) break;
8365           if (p == npt) ptpoints[npt++] = ccone[ccp];
8366         }
8367       }
8368     }
8369     break;
8370   default: break;
8371   }
8372   for (pt = 0; pt < npt; ++pt) {
8373     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8374     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8375   }
8376   PetscFunctionReturn(0);
8377 }
8378 
8379 /*@
8380   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8381 
8382   Input Parameters:
8383 + dm - The DMPlex object
8384 - cellHeight - Normally 0
8385 
8386   Notes:
8387   This is a useful diagnostic when creating meshes programmatically.
8388   Currently applicable only to homogeneous simplex or tensor meshes.
8389 
8390   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8391 
8392   Level: developer
8393 
8394 .seealso: `DMCreate()`, `DMSetFromOptions()`
8395 @*/
8396 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight) {
8397   DMPlexInterpolatedFlag interp;
8398   DMPolytopeType         ct;
8399   PetscInt               vStart, vEnd, cStart, cEnd, c;
8400 
8401   PetscFunctionBegin;
8402   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8403   PetscCall(DMPlexIsInterpolated(dm, &interp));
8404   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8405   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8406   for (c = cStart; c < cEnd; ++c) {
8407     PetscInt *closure = NULL;
8408     PetscInt  coneSize, closureSize, cl, Nv = 0;
8409 
8410     PetscCall(DMPlexGetCellType(dm, c, &ct));
8411     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8412     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8413     if (interp == DMPLEX_INTERPOLATED_FULL) {
8414       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8415       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));
8416     }
8417     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8418     for (cl = 0; cl < closureSize * 2; cl += 2) {
8419       const PetscInt p = closure[cl];
8420       if ((p >= vStart) && (p < vEnd)) ++Nv;
8421     }
8422     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8423     /* Special Case: Tensor faces with identified vertices */
8424     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8425       PetscInt unsplit;
8426 
8427       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8428       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8429     }
8430     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));
8431   }
8432   PetscFunctionReturn(0);
8433 }
8434 
8435 /*@
8436   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8437 
8438   Collective
8439 
8440   Input Parameters:
8441 + dm - The DMPlex object
8442 - cellHeight - Normally 0
8443 
8444   Notes:
8445   This is a useful diagnostic when creating meshes programmatically.
8446   This routine is only relevant for meshes that are fully interpolated across all ranks.
8447   It will error out if a partially interpolated mesh is given on some rank.
8448   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8449 
8450   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8451 
8452   Level: developer
8453 
8454 .seealso: `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8455 @*/
8456 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight) {
8457   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8458   DMPlexInterpolatedFlag interpEnum;
8459 
8460   PetscFunctionBegin;
8461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8462   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8463   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(0);
8464   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8465     PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported");
8466     PetscFunctionReturn(0);
8467   }
8468 
8469   PetscCall(DMGetDimension(dm, &dim));
8470   PetscCall(DMPlexGetDepth(dm, &depth));
8471   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8472   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8473     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8474     for (c = cStart; c < cEnd; ++c) {
8475       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8476       const DMPolytopeType *faceTypes;
8477       DMPolytopeType        ct;
8478       PetscInt              numFaces, coneSize, f;
8479       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8480 
8481       PetscCall(DMPlexGetCellType(dm, c, &ct));
8482       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8483       if (unsplit) continue;
8484       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8485       PetscCall(DMPlexGetCone(dm, c, &cone));
8486       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8487       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8488       for (cl = 0; cl < closureSize * 2; cl += 2) {
8489         const PetscInt p = closure[cl];
8490         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8491       }
8492       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8493       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);
8494       for (f = 0; f < numFaces; ++f) {
8495         DMPolytopeType fct;
8496         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8497 
8498         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8499         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8500         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8501           const PetscInt p = fclosure[cl];
8502           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8503         }
8504         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]);
8505         for (v = 0; v < fnumCorners; ++v) {
8506           if (fclosure[v] != faces[fOff + v]) {
8507             PetscInt v1;
8508 
8509             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8510             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8511             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8512             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8513             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8514             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]);
8515           }
8516         }
8517         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8518         fOff += faceSizes[f];
8519       }
8520       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8521       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8522     }
8523   }
8524   PetscFunctionReturn(0);
8525 }
8526 
8527 /*@
8528   DMPlexCheckGeometry - Check the geometry of mesh cells
8529 
8530   Input Parameter:
8531 . dm - The DMPlex object
8532 
8533   Notes:
8534   This is a useful diagnostic when creating meshes programmatically.
8535 
8536   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8537 
8538   Level: developer
8539 
8540 .seealso: `DMCreate()`, `DMSetFromOptions()`
8541 @*/
8542 PetscErrorCode DMPlexCheckGeometry(DM dm) {
8543   Vec       coordinates;
8544   PetscReal detJ, J[9], refVol = 1.0;
8545   PetscReal vol;
8546   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8547 
8548   PetscFunctionBegin;
8549   PetscCall(DMGetDimension(dm, &dim));
8550   PetscCall(DMGetCoordinateDim(dm, &dE));
8551   if (dim != dE) PetscFunctionReturn(0);
8552   PetscCall(DMPlexGetDepth(dm, &depth));
8553   for (d = 0; d < dim; ++d) refVol *= 2.0;
8554   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8555   /* Make sure local coordinates are created, because that step is collective */
8556   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8557   for (c = cStart; c < cEnd; ++c) {
8558     DMPolytopeType ct;
8559     PetscInt       unsplit;
8560     PetscBool      ignoreZeroVol = PETSC_FALSE;
8561 
8562     PetscCall(DMPlexGetCellType(dm, c, &ct));
8563     switch (ct) {
8564     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8565     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8566     case DM_POLYTOPE_QUAD_PRISM_TENSOR: ignoreZeroVol = PETSC_TRUE; break;
8567     default: break;
8568     }
8569     switch (ct) {
8570     case DM_POLYTOPE_TRI_PRISM:
8571     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8572     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8573     case DM_POLYTOPE_PYRAMID: continue;
8574     default: break;
8575     }
8576     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8577     if (unsplit) continue;
8578     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
8579     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);
8580     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
8581     /* This should work with periodicity since DG coordinates should be used */
8582     if (depth > 1) {
8583       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
8584       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);
8585       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
8586     }
8587   }
8588   PetscFunctionReturn(0);
8589 }
8590 
8591 /*@
8592   DMPlexCheckPointSF - Check that several necessary conditions are met for the Point SF of this plex.
8593 
8594   Collective
8595 
8596   Input Parameters:
8597 + dm - The DMPlex object
8598 . pointSF - The Point SF, or NULL for Point SF attached to DM
8599 - allowExtraRoots - Flag to allow extra points not present in the DM
8600 
8601   Notes:
8602   This is mainly intended for debugging/testing purposes.
8603 
8604   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8605 
8606   Extra roots can come from priodic cuts, where additional points appear on the boundary
8607 
8608   Level: developer
8609 
8610 .seealso: `DMGetPointSF()`, `DMSetFromOptions()`
8611 @*/
8612 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots) {
8613   PetscInt           l, nleaves, nroots, overlap;
8614   const PetscInt    *locals;
8615   const PetscSFNode *remotes;
8616   PetscBool          distributed;
8617   MPI_Comm           comm;
8618   PetscMPIInt        rank;
8619 
8620   PetscFunctionBegin;
8621   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8622   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
8623   else pointSF = dm->sf;
8624   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8625   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
8626   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8627   {
8628     PetscMPIInt mpiFlag;
8629 
8630     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
8631     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
8632   }
8633   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
8634   PetscCall(DMPlexIsDistributed(dm, &distributed));
8635   if (!distributed) {
8636     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);
8637     PetscFunctionReturn(0);
8638   }
8639   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);
8640   PetscCall(DMPlexGetOverlap(dm, &overlap));
8641 
8642   /* Check SF graph is compatible with DMPlex chart */
8643   {
8644     PetscInt pStart, pEnd, maxLeaf;
8645 
8646     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8647     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
8648     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
8649     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
8650   }
8651 
8652   /* Check Point SF has no local points referenced */
8653   for (l = 0; l < nleaves; l++) {
8654     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);
8655   }
8656 
8657   /* Check there are no cells in interface */
8658   if (!overlap) {
8659     PetscInt cellHeight, cStart, cEnd;
8660 
8661     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8662     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8663     for (l = 0; l < nleaves; ++l) {
8664       const PetscInt point = locals ? locals[l] : l;
8665 
8666       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
8667     }
8668   }
8669 
8670   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
8671   {
8672     const PetscInt *rootdegree;
8673 
8674     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
8675     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
8676     for (l = 0; l < nleaves; ++l) {
8677       const PetscInt  point = locals ? locals[l] : l;
8678       const PetscInt *cone;
8679       PetscInt        coneSize, c, idx;
8680 
8681       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
8682       PetscCall(DMPlexGetCone(dm, point, &cone));
8683       for (c = 0; c < coneSize; ++c) {
8684         if (!rootdegree[cone[c]]) {
8685           if (locals) {
8686             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
8687           } else {
8688             idx = (cone[c] < nleaves) ? cone[c] : -1;
8689           }
8690           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
8691         }
8692       }
8693     }
8694   }
8695   PetscFunctionReturn(0);
8696 }
8697 
8698 /*@
8699   DMPlexCheck - Perform various checks of Plex sanity
8700 
8701   Input Parameter:
8702 . dm - The DMPlex object
8703 
8704   Notes:
8705   This is a useful diagnostic when creating meshes programmatically.
8706 
8707   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8708 
8709   Currently does not include DMPlexCheckCellShape().
8710 
8711   Level: developer
8712 
8713 .seealso: DMCreate(), DMSetFromOptions()
8714 @*/
8715 PetscErrorCode DMPlexCheck(DM dm) {
8716   PetscInt cellHeight;
8717 
8718   PetscFunctionBegin;
8719   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8720   PetscCall(DMPlexCheckSymmetry(dm));
8721   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
8722   PetscCall(DMPlexCheckFaces(dm, cellHeight));
8723   PetscCall(DMPlexCheckGeometry(dm));
8724   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
8725   PetscCall(DMPlexCheckInterfaceCones(dm));
8726   PetscFunctionReturn(0);
8727 }
8728 
8729 typedef struct cell_stats {
8730   PetscReal min, max, sum, squaresum;
8731   PetscInt  count;
8732 } cell_stats_t;
8733 
8734 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype) {
8735   PetscInt i, N = *len;
8736 
8737   for (i = 0; i < N; i++) {
8738     cell_stats_t *A = (cell_stats_t *)a;
8739     cell_stats_t *B = (cell_stats_t *)b;
8740 
8741     B->min = PetscMin(A->min, B->min);
8742     B->max = PetscMax(A->max, B->max);
8743     B->sum += A->sum;
8744     B->squaresum += A->squaresum;
8745     B->count += A->count;
8746   }
8747 }
8748 
8749 /*@
8750   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
8751 
8752   Collective on dm
8753 
8754   Input Parameters:
8755 + dm        - The DMPlex object
8756 . output    - If true, statistics will be displayed on stdout
8757 - condLimit - Display all cells above this condition number, or PETSC_DETERMINE for no cell output
8758 
8759   Notes:
8760   This is mainly intended for debugging/testing purposes.
8761 
8762   For the complete list of DMPlexCheck* functions, see DMSetFromOptions().
8763 
8764   Level: developer
8765 
8766 .seealso: `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
8767 @*/
8768 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit) {
8769   DM           dmCoarse;
8770   cell_stats_t stats, globalStats;
8771   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
8772   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
8773   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
8774   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
8775   PetscMPIInt  rank, size;
8776 
8777   PetscFunctionBegin;
8778   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8779   stats.min = PETSC_MAX_REAL;
8780   stats.max = PETSC_MIN_REAL;
8781   stats.sum = stats.squaresum = 0.;
8782   stats.count                 = 0;
8783 
8784   PetscCallMPI(MPI_Comm_size(comm, &size));
8785   PetscCallMPI(MPI_Comm_rank(comm, &rank));
8786   PetscCall(DMGetCoordinateDim(dm, &cdim));
8787   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
8788   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
8789   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8790   for (c = cStart; c < cEnd; c++) {
8791     PetscInt  i;
8792     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
8793 
8794     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
8795     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
8796     for (i = 0; i < PetscSqr(cdim); ++i) {
8797       frobJ += J[i] * J[i];
8798       frobInvJ += invJ[i] * invJ[i];
8799     }
8800     cond2 = frobJ * frobInvJ;
8801     cond  = PetscSqrtReal(cond2);
8802 
8803     stats.min = PetscMin(stats.min, cond);
8804     stats.max = PetscMax(stats.max, cond);
8805     stats.sum += cond;
8806     stats.squaresum += cond2;
8807     stats.count++;
8808     if (output && cond > limit) {
8809       PetscSection coordSection;
8810       Vec          coordsLocal;
8811       PetscScalar *coords = NULL;
8812       PetscInt     Nv, d, clSize, cl, *closure = NULL;
8813 
8814       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
8815       PetscCall(DMGetCoordinateSection(dm, &coordSection));
8816       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8817       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
8818       for (i = 0; i < Nv / cdim; ++i) {
8819         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
8820         for (d = 0; d < cdim; ++d) {
8821           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
8822           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
8823         }
8824         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
8825       }
8826       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8827       for (cl = 0; cl < clSize * 2; cl += 2) {
8828         const PetscInt edge = closure[cl];
8829 
8830         if ((edge >= eStart) && (edge < eEnd)) {
8831           PetscReal len;
8832 
8833           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
8834           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
8835         }
8836       }
8837       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
8838       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
8839     }
8840   }
8841   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
8842 
8843   if (size > 1) {
8844     PetscMPIInt  blockLengths[2] = {4, 1};
8845     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
8846     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
8847     MPI_Op       statReduce;
8848 
8849     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
8850     PetscCallMPI(MPI_Type_commit(&statType));
8851     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
8852     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
8853     PetscCallMPI(MPI_Op_free(&statReduce));
8854     PetscCallMPI(MPI_Type_free(&statType));
8855   } else {
8856     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
8857   }
8858   if (rank == 0) {
8859     count = globalStats.count;
8860     min   = globalStats.min;
8861     max   = globalStats.max;
8862     mean  = globalStats.sum / globalStats.count;
8863     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
8864   }
8865 
8866   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));
8867   PetscCall(PetscFree2(J, invJ));
8868 
8869   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
8870   if (dmCoarse) {
8871     PetscBool isplex;
8872 
8873     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
8874     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
8875   }
8876   PetscFunctionReturn(0);
8877 }
8878 
8879 /*@
8880   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
8881   orthogonal quality below given tolerance.
8882 
8883   Collective on dm
8884 
8885   Input Parameters:
8886 + dm   - The DMPlex object
8887 . fv   - Optional PetscFV object for pre-computed cell/face centroid information
8888 - atol - [0, 1] Absolute tolerance for tagging cells.
8889 
8890   Output Parameters:
8891 + OrthQual      - Vec containing orthogonal quality per cell
8892 - OrthQualLabel - DMLabel tagging cells below atol with DM_ADAPT_REFINE
8893 
8894   Options Database Keys:
8895 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only PETSCVIEWERASCII is
8896 supported.
8897 - -dm_plex_orthogonal_quality_vec_view - view OrthQual vector.
8898 
8899   Notes:
8900   Orthogonal quality is given by the following formula:
8901 
8902   \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]
8903 
8904   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
8905   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
8906   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
8907   calculating the cosine of the angle between these vectors.
8908 
8909   Orthogonal quality ranges from 1 (best) to 0 (worst).
8910 
8911   This routine is mainly useful for FVM, however is not restricted to only FVM. The PetscFV object is optionally used to check for
8912   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
8913 
8914   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
8915 
8916   Level: intermediate
8917 
8918 .seealso: `DMPlexCheckCellShape()`, `DMCreateLabel()`
8919 @*/
8920 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel) {
8921   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
8922   PetscInt              *idx;
8923   PetscScalar           *oqVals;
8924   const PetscScalar     *cellGeomArr, *faceGeomArr;
8925   PetscReal             *ci, *fi, *Ai;
8926   MPI_Comm               comm;
8927   Vec                    cellgeom, facegeom;
8928   DM                     dmFace, dmCell;
8929   IS                     glob;
8930   ISLocalToGlobalMapping ltog;
8931   PetscViewer            vwr;
8932 
8933   PetscFunctionBegin;
8934   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8935   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
8936   PetscValidPointer(OrthQual, 4);
8937   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
8938   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
8939   PetscCall(DMGetDimension(dm, &nc));
8940   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
8941   {
8942     DMPlexInterpolatedFlag interpFlag;
8943 
8944     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
8945     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
8946       PetscMPIInt rank;
8947 
8948       PetscCallMPI(MPI_Comm_rank(comm, &rank));
8949       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
8950     }
8951   }
8952   if (OrthQualLabel) {
8953     PetscValidPointer(OrthQualLabel, 5);
8954     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
8955     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
8956   } else {
8957     *OrthQualLabel = NULL;
8958   }
8959   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8960   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8961   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
8962   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
8963   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
8964   PetscCall(VecCreate(comm, OrthQual));
8965   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
8966   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
8967   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
8968   PetscCall(VecSetUp(*OrthQual));
8969   PetscCall(ISDestroy(&glob));
8970   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
8971   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
8972   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
8973   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
8974   PetscCall(VecGetDM(cellgeom, &dmCell));
8975   PetscCall(VecGetDM(facegeom, &dmFace));
8976   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
8977   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
8978     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
8979     PetscInt         cellarr[2], *adj = NULL;
8980     PetscScalar     *cArr, *fArr;
8981     PetscReal        minvalc = 1.0, minvalf = 1.0;
8982     PetscFVCellGeom *cg;
8983 
8984     idx[cellIter] = cell - cStart;
8985     cellarr[0]    = cell;
8986     /* Make indexing into cellGeom easier */
8987     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
8988     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
8989     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
8990     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
8991     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
8992       PetscInt         i;
8993       const PetscInt   neigh  = adj[cellneigh];
8994       PetscReal        normci = 0, normfi = 0, normai = 0;
8995       PetscFVCellGeom *cgneigh;
8996       PetscFVFaceGeom *fg;
8997 
8998       /* Don't count ourselves in the neighbor list */
8999       if (neigh == cell) continue;
9000       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9001       cellarr[1] = neigh;
9002       {
9003         PetscInt        numcovpts;
9004         const PetscInt *covpts;
9005 
9006         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9007         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9008         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9009       }
9010 
9011       /* Compute c_i, f_i and their norms */
9012       for (i = 0; i < nc; i++) {
9013         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9014         fi[i] = fg->centroid[i] - cg->centroid[i];
9015         Ai[i] = fg->normal[i];
9016         normci += PetscPowReal(ci[i], 2);
9017         normfi += PetscPowReal(fi[i], 2);
9018         normai += PetscPowReal(Ai[i], 2);
9019       }
9020       normci = PetscSqrtReal(normci);
9021       normfi = PetscSqrtReal(normfi);
9022       normai = PetscSqrtReal(normai);
9023 
9024       /* Normalize and compute for each face-cell-normal pair */
9025       for (i = 0; i < nc; i++) {
9026         ci[i] = ci[i] / normci;
9027         fi[i] = fi[i] / normfi;
9028         Ai[i] = Ai[i] / normai;
9029         /* PetscAbs because I don't know if normals are guaranteed to point out */
9030         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9031         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9032       }
9033       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9034       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9035     }
9036     PetscCall(PetscFree(adj));
9037     PetscCall(PetscFree2(cArr, fArr));
9038     /* Defer to cell if they're equal */
9039     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9040     if (OrthQualLabel) {
9041       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9042     }
9043   }
9044   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9045   PetscCall(VecAssemblyBegin(*OrthQual));
9046   PetscCall(VecAssemblyEnd(*OrthQual));
9047   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9048   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9049   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9050   if (OrthQualLabel) {
9051     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9052   }
9053   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9054   PetscCall(PetscViewerDestroy(&vwr));
9055   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9056   PetscFunctionReturn(0);
9057 }
9058 
9059 /* this is here insead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9060  * interpolator construction */
9061 static PetscErrorCode DMGetFullDM(DM dm, DM *odm) {
9062   PetscSection section, newSection, gsection;
9063   PetscSF      sf;
9064   PetscBool    hasConstraints, ghasConstraints;
9065 
9066   PetscFunctionBegin;
9067   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9068   PetscValidPointer(odm, 2);
9069   PetscCall(DMGetLocalSection(dm, &section));
9070   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9071   PetscCallMPI(MPI_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9072   if (!ghasConstraints) {
9073     PetscCall(PetscObjectReference((PetscObject)dm));
9074     *odm = dm;
9075     PetscFunctionReturn(0);
9076   }
9077   PetscCall(DMClone(dm, odm));
9078   PetscCall(DMCopyFields(dm, *odm));
9079   PetscCall(DMGetLocalSection(*odm, &newSection));
9080   PetscCall(DMGetPointSF(*odm, &sf));
9081   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9082   PetscCall(DMSetGlobalSection(*odm, gsection));
9083   PetscCall(PetscSectionDestroy(&gsection));
9084   PetscFunctionReturn(0);
9085 }
9086 
9087 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift) {
9088   DM        dmco, dmfo;
9089   Mat       interpo;
9090   Vec       rscale;
9091   Vec       cglobalo, clocal;
9092   Vec       fglobal, fglobalo, flocal;
9093   PetscBool regular;
9094 
9095   PetscFunctionBegin;
9096   PetscCall(DMGetFullDM(dmc, &dmco));
9097   PetscCall(DMGetFullDM(dmf, &dmfo));
9098   PetscCall(DMSetCoarseDM(dmfo, dmco));
9099   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9100   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9101   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9102   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9103   PetscCall(DMCreateLocalVector(dmc, &clocal));
9104   PetscCall(VecSet(cglobalo, 0.));
9105   PetscCall(VecSet(clocal, 0.));
9106   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9107   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9108   PetscCall(DMCreateLocalVector(dmf, &flocal));
9109   PetscCall(VecSet(fglobal, 0.));
9110   PetscCall(VecSet(fglobalo, 0.));
9111   PetscCall(VecSet(flocal, 0.));
9112   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9113   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9114   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9115   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9116   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9117   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9118   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9119   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9120   *shift = fglobal;
9121   PetscCall(VecDestroy(&flocal));
9122   PetscCall(VecDestroy(&fglobalo));
9123   PetscCall(VecDestroy(&clocal));
9124   PetscCall(VecDestroy(&cglobalo));
9125   PetscCall(VecDestroy(&rscale));
9126   PetscCall(MatDestroy(&interpo));
9127   PetscCall(DMDestroy(&dmfo));
9128   PetscCall(DMDestroy(&dmco));
9129   PetscFunctionReturn(0);
9130 }
9131 
9132 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol) {
9133   PetscObject shifto;
9134   Vec         shift;
9135 
9136   PetscFunctionBegin;
9137   if (!interp) {
9138     Vec rscale;
9139 
9140     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9141     PetscCall(VecDestroy(&rscale));
9142   } else {
9143     PetscCall(PetscObjectReference((PetscObject)interp));
9144   }
9145   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9146   if (!shifto) {
9147     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9148     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9149     shifto = (PetscObject)shift;
9150     PetscCall(VecDestroy(&shift));
9151   }
9152   shift = (Vec)shifto;
9153   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9154   PetscCall(VecAXPY(fineSol, 1.0, shift));
9155   PetscCall(MatDestroy(&interp));
9156   PetscFunctionReturn(0);
9157 }
9158 
9159 /* Pointwise interpolation
9160      Just code FEM for now
9161      u^f = I u^c
9162      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9163      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9164      I_{ij} = psi^f_i phi^c_j
9165 */
9166 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling) {
9167   PetscSection gsc, gsf;
9168   PetscInt     m, n;
9169   void        *ctx;
9170   DM           cdm;
9171   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9172 
9173   PetscFunctionBegin;
9174   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9175   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9176   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9177   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9178 
9179   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9180   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9181   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9182   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9183   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9184 
9185   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9186   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9187   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9188   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9189   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9190   if (scaling) {
9191     /* Use naive scaling */
9192     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9193   }
9194   PetscFunctionReturn(0);
9195 }
9196 
9197 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat) {
9198   VecScatter ctx;
9199 
9200   PetscFunctionBegin;
9201   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9202   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9203   PetscCall(VecScatterDestroy(&ctx));
9204   PetscFunctionReturn(0);
9205 }
9206 
9207 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[]) {
9208   const PetscInt Nc = uOff[1] - uOff[0];
9209   PetscInt       c;
9210   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9211 }
9212 
9213 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass) {
9214   DM           dmc;
9215   PetscDS      ds;
9216   Vec          ones, locmass;
9217   IS           cellIS;
9218   PetscFormKey key;
9219   PetscInt     depth;
9220 
9221   PetscFunctionBegin;
9222   PetscCall(DMClone(dm, &dmc));
9223   PetscCall(DMCopyDisc(dm, dmc));
9224   PetscCall(DMGetDS(dmc, &ds));
9225   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9226   PetscCall(DMCreateGlobalVector(dmc, mass));
9227   PetscCall(DMGetLocalVector(dmc, &ones));
9228   PetscCall(DMGetLocalVector(dmc, &locmass));
9229   PetscCall(DMPlexGetDepth(dmc, &depth));
9230   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9231   PetscCall(VecSet(locmass, 0.0));
9232   PetscCall(VecSet(ones, 1.0));
9233   key.label = NULL;
9234   key.value = 0;
9235   key.field = 0;
9236   key.part  = 0;
9237   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9238   PetscCall(ISDestroy(&cellIS));
9239   PetscCall(VecSet(*mass, 0.0));
9240   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9241   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9242   PetscCall(DMRestoreLocalVector(dmc, &ones));
9243   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9244   PetscCall(DMDestroy(&dmc));
9245   PetscFunctionReturn(0);
9246 }
9247 
9248 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass) {
9249   PetscSection gsc, gsf;
9250   PetscInt     m, n;
9251   void        *ctx;
9252   DM           cdm;
9253   PetscBool    regular;
9254 
9255   PetscFunctionBegin;
9256   if (dmFine == dmCoarse) {
9257     DM            dmc;
9258     PetscDS       ds;
9259     PetscWeakForm wf;
9260     Vec           u;
9261     IS            cellIS;
9262     PetscFormKey  key;
9263     PetscInt      depth;
9264 
9265     PetscCall(DMClone(dmFine, &dmc));
9266     PetscCall(DMCopyDisc(dmFine, dmc));
9267     PetscCall(DMGetDS(dmc, &ds));
9268     PetscCall(PetscDSGetWeakForm(ds, &wf));
9269     PetscCall(PetscWeakFormClear(wf));
9270     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9271     PetscCall(DMCreateMatrix(dmc, mass));
9272     PetscCall(DMGetLocalVector(dmc, &u));
9273     PetscCall(DMPlexGetDepth(dmc, &depth));
9274     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9275     PetscCall(MatZeroEntries(*mass));
9276     key.label = NULL;
9277     key.value = 0;
9278     key.field = 0;
9279     key.part  = 0;
9280     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9281     PetscCall(ISDestroy(&cellIS));
9282     PetscCall(DMRestoreLocalVector(dmc, &u));
9283     PetscCall(DMDestroy(&dmc));
9284   } else {
9285     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9286     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9287     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9288     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9289 
9290     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9291     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9292     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9293     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9294 
9295     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9296     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9297     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9298     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9299   }
9300   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9301   PetscFunctionReturn(0);
9302 }
9303 
9304 /*@
9305   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9306 
9307   Input Parameter:
9308 . dm - The DMPlex object
9309 
9310   Output Parameter:
9311 . regular - The flag
9312 
9313   Level: intermediate
9314 
9315 .seealso: `DMPlexSetRegularRefinement()`
9316 @*/
9317 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular) {
9318   PetscFunctionBegin;
9319   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9320   PetscValidBoolPointer(regular, 2);
9321   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9322   PetscFunctionReturn(0);
9323 }
9324 
9325 /*@
9326   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9327 
9328   Input Parameters:
9329 + dm - The DMPlex object
9330 - regular - The flag
9331 
9332   Level: intermediate
9333 
9334 .seealso: `DMPlexGetRegularRefinement()`
9335 @*/
9336 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular) {
9337   PetscFunctionBegin;
9338   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9339   ((DM_Plex *)dm->data)->regularRefinement = regular;
9340   PetscFunctionReturn(0);
9341 }
9342 
9343 /* anchors */
9344 /*@
9345   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9346   call DMPlexGetAnchors() directly: if there are anchors, then DMPlexGetAnchors() is called during DMGetDefaultConstraints().
9347 
9348   not collective
9349 
9350   Input Parameter:
9351 . dm - The DMPlex object
9352 
9353   Output Parameters:
9354 + anchorSection - If not NULL, set to the section describing which points anchor the constrained points.
9355 - anchorIS - If not NULL, set to the list of anchors indexed by anchorSection
9356 
9357   Level: intermediate
9358 
9359 .seealso: `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9360 @*/
9361 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS) {
9362   DM_Plex *plex = (DM_Plex *)dm->data;
9363 
9364   PetscFunctionBegin;
9365   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9366   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9367   if (anchorSection) *anchorSection = plex->anchorSection;
9368   if (anchorIS) *anchorIS = plex->anchorIS;
9369   PetscFunctionReturn(0);
9370 }
9371 
9372 /*@
9373   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.  Unlike boundary conditions,
9374   when a point's degrees of freedom in a section are constrained to an outside value, the anchor constraints set a
9375   point's degrees of freedom to be a linear combination of other points' degrees of freedom.
9376 
9377   After specifying the layout of constraints with DMPlexSetAnchors(), one specifies the constraints by calling
9378   DMGetDefaultConstraints() and filling in the entries in the constraint matrix.
9379 
9380   collective on dm
9381 
9382   Input Parameters:
9383 + dm - The DMPlex object
9384 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9385 - anchorIS - The list of all anchor points.  Must have a local communicator (PETSC_COMM_SELF or derivative).
9386 
9387   The reference counts of anchorSection and anchorIS are incremented.
9388 
9389   Level: intermediate
9390 
9391 .seealso: `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9392 @*/
9393 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS) {
9394   DM_Plex    *plex = (DM_Plex *)dm->data;
9395   PetscMPIInt result;
9396 
9397   PetscFunctionBegin;
9398   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9399   if (anchorSection) {
9400     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9401     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9402     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9403   }
9404   if (anchorIS) {
9405     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9406     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9407     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9408   }
9409 
9410   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9411   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9412   plex->anchorSection = anchorSection;
9413 
9414   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9415   PetscCall(ISDestroy(&plex->anchorIS));
9416   plex->anchorIS = anchorIS;
9417 
9418   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9419     PetscInt        size, a, pStart, pEnd;
9420     const PetscInt *anchors;
9421 
9422     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9423     PetscCall(ISGetLocalSize(anchorIS, &size));
9424     PetscCall(ISGetIndices(anchorIS, &anchors));
9425     for (a = 0; a < size; a++) {
9426       PetscInt p;
9427 
9428       p = anchors[a];
9429       if (p >= pStart && p < pEnd) {
9430         PetscInt dof;
9431 
9432         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9433         if (dof) {
9434           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9435           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9436         }
9437       }
9438     }
9439     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9440   }
9441   /* reset the generic constraints */
9442   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9443   PetscFunctionReturn(0);
9444 }
9445 
9446 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec) {
9447   PetscSection anchorSection;
9448   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9449 
9450   PetscFunctionBegin;
9451   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9452   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9453   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9454   PetscCall(PetscSectionGetNumFields(section, &numFields));
9455   if (numFields) {
9456     PetscInt f;
9457     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9458 
9459     for (f = 0; f < numFields; f++) {
9460       PetscInt numComp;
9461 
9462       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9463       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9464     }
9465   }
9466   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9467   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9468   pStart = PetscMax(pStart, sStart);
9469   pEnd   = PetscMin(pEnd, sEnd);
9470   pEnd   = PetscMax(pStart, pEnd);
9471   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9472   for (p = pStart; p < pEnd; p++) {
9473     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9474     if (dof) {
9475       PetscCall(PetscSectionGetDof(section, p, &dof));
9476       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9477       for (f = 0; f < numFields; f++) {
9478         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9479         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9480       }
9481     }
9482   }
9483   PetscCall(PetscSectionSetUp(*cSec));
9484   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9485   PetscFunctionReturn(0);
9486 }
9487 
9488 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat) {
9489   PetscSection    aSec;
9490   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9491   const PetscInt *anchors;
9492   PetscInt        numFields, f;
9493   IS              aIS;
9494   MatType         mtype;
9495   PetscBool       iscuda, iskokkos;
9496 
9497   PetscFunctionBegin;
9498   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9499   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9500   PetscCall(PetscSectionGetStorageSize(section, &n));
9501   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9502   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9503   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9504   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9505   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9506   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9507   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9508   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9509   else mtype = MATSEQAIJ;
9510   PetscCall(MatSetType(*cMat, mtype));
9511   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9512   PetscCall(ISGetIndices(aIS, &anchors));
9513   /* cSec will be a subset of aSec and section */
9514   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9515   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9516   PetscCall(PetscMalloc1(m + 1, &i));
9517   i[0] = 0;
9518   PetscCall(PetscSectionGetNumFields(section, &numFields));
9519   for (p = pStart; p < pEnd; p++) {
9520     PetscInt rDof, rOff, r;
9521 
9522     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9523     if (!rDof) continue;
9524     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9525     if (numFields) {
9526       for (f = 0; f < numFields; f++) {
9527         annz = 0;
9528         for (r = 0; r < rDof; r++) {
9529           a = anchors[rOff + r];
9530           if (a < sStart || a >= sEnd) continue;
9531           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9532           annz += aDof;
9533         }
9534         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9535         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9536         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9537       }
9538     } else {
9539       annz = 0;
9540       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9541       for (q = 0; q < dof; q++) {
9542         a = anchors[rOff + q];
9543         if (a < sStart || a >= sEnd) continue;
9544         PetscCall(PetscSectionGetDof(section, a, &aDof));
9545         annz += aDof;
9546       }
9547       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9548       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9549       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9550     }
9551   }
9552   nnz = i[m];
9553   PetscCall(PetscMalloc1(nnz, &j));
9554   offset = 0;
9555   for (p = pStart; p < pEnd; p++) {
9556     if (numFields) {
9557       for (f = 0; f < numFields; f++) {
9558         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9559         for (q = 0; q < dof; q++) {
9560           PetscInt rDof, rOff, r;
9561           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9562           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9563           for (r = 0; r < rDof; r++) {
9564             PetscInt s;
9565 
9566             a = anchors[rOff + r];
9567             if (a < sStart || a >= sEnd) continue;
9568             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9569             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
9570             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9571           }
9572         }
9573       }
9574     } else {
9575       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9576       for (q = 0; q < dof; q++) {
9577         PetscInt rDof, rOff, r;
9578         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9579         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9580         for (r = 0; r < rDof; r++) {
9581           PetscInt s;
9582 
9583           a = anchors[rOff + r];
9584           if (a < sStart || a >= sEnd) continue;
9585           PetscCall(PetscSectionGetDof(section, a, &aDof));
9586           PetscCall(PetscSectionGetOffset(section, a, &aOff));
9587           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
9588         }
9589       }
9590     }
9591   }
9592   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
9593   PetscCall(PetscFree(i));
9594   PetscCall(PetscFree(j));
9595   PetscCall(ISRestoreIndices(aIS, &anchors));
9596   PetscFunctionReturn(0);
9597 }
9598 
9599 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm) {
9600   DM_Plex     *plex = (DM_Plex *)dm->data;
9601   PetscSection anchorSection, section, cSec;
9602   Mat          cMat;
9603 
9604   PetscFunctionBegin;
9605   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9606   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9607   if (anchorSection) {
9608     PetscInt Nf;
9609 
9610     PetscCall(DMGetLocalSection(dm, &section));
9611     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
9612     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
9613     PetscCall(DMGetNumFields(dm, &Nf));
9614     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
9615     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
9616     PetscCall(PetscSectionDestroy(&cSec));
9617     PetscCall(MatDestroy(&cMat));
9618   }
9619   PetscFunctionReturn(0);
9620 }
9621 
9622 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm) {
9623   IS           subis;
9624   PetscSection section, subsection;
9625 
9626   PetscFunctionBegin;
9627   PetscCall(DMGetLocalSection(dm, &section));
9628   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
9629   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
9630   /* Create subdomain */
9631   PetscCall(DMPlexFilter(dm, label, value, subdm));
9632   /* Create submodel */
9633   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
9634   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
9635   PetscCall(DMSetLocalSection(*subdm, subsection));
9636   PetscCall(PetscSectionDestroy(&subsection));
9637   PetscCall(DMCopyDisc(dm, *subdm));
9638   /* Create map from submodel to global model */
9639   if (is) {
9640     PetscSection    sectionGlobal, subsectionGlobal;
9641     IS              spIS;
9642     const PetscInt *spmap;
9643     PetscInt       *subIndices;
9644     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
9645     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
9646 
9647     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
9648     PetscCall(ISGetIndices(spIS, &spmap));
9649     PetscCall(PetscSectionGetNumFields(section, &Nf));
9650     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
9651     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
9652     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
9653     for (p = pStart; p < pEnd; ++p) {
9654       PetscInt gdof, pSubSize = 0;
9655 
9656       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
9657       if (gdof > 0) {
9658         for (f = 0; f < Nf; ++f) {
9659           PetscInt fdof, fcdof;
9660 
9661           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
9662           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
9663           pSubSize += fdof - fcdof;
9664         }
9665         subSize += pSubSize;
9666         if (pSubSize) {
9667           if (bs < 0) {
9668             bs = pSubSize;
9669           } else if (bs != pSubSize) {
9670             /* Layout does not admit a pointwise block size */
9671             bs = 1;
9672           }
9673         }
9674       }
9675     }
9676     /* Must have same blocksize on all procs (some might have no points) */
9677     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
9678     bsLocal[1] = bs;
9679     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
9680     if (bsMinMax[0] != bsMinMax[1]) {
9681       bs = 1;
9682     } else {
9683       bs = bsMinMax[0];
9684     }
9685     PetscCall(PetscMalloc1(subSize, &subIndices));
9686     for (p = pStart; p < pEnd; ++p) {
9687       PetscInt gdof, goff;
9688 
9689       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
9690       if (gdof > 0) {
9691         const PetscInt point = spmap[p];
9692 
9693         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
9694         for (f = 0; f < Nf; ++f) {
9695           PetscInt fdof, fcdof, fc, f2, poff = 0;
9696 
9697           /* Can get rid of this loop by storing field information in the global section */
9698           for (f2 = 0; f2 < f; ++f2) {
9699             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
9700             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
9701             poff += fdof - fcdof;
9702           }
9703           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
9704           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
9705           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
9706         }
9707       }
9708     }
9709     PetscCall(ISRestoreIndices(spIS, &spmap));
9710     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
9711     if (bs > 1) {
9712       /* We need to check that the block size does not come from non-contiguous fields */
9713       PetscInt i, j, set = 1;
9714       for (i = 0; i < subSize; i += bs) {
9715         for (j = 0; j < bs; ++j) {
9716           if (subIndices[i + j] != subIndices[i] + j) {
9717             set = 0;
9718             break;
9719           }
9720         }
9721       }
9722       if (set) PetscCall(ISSetBlockSize(*is, bs));
9723     }
9724     /* Attach nullspace */
9725     for (f = 0; f < Nf; ++f) {
9726       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
9727       if ((*subdm)->nullspaceConstructors[f]) break;
9728     }
9729     if (f < Nf) {
9730       MatNullSpace nullSpace;
9731       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
9732 
9733       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
9734       PetscCall(MatNullSpaceDestroy(&nullSpace));
9735     }
9736   }
9737   PetscFunctionReturn(0);
9738 }
9739 
9740 /*@
9741   DMPlexMonitorThroughput - Report the cell throughput of FE integration
9742 
9743   Input Parameter:
9744 - dm - The DM
9745 
9746   Level: developer
9747 
9748   Options Database Keys:
9749 . -dm_plex_monitor_throughput - Activate the monitor
9750 
9751 .seealso: `DMSetFromOptions()`, `DMPlexCreate()`
9752 @*/
9753 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy) {
9754 #if defined(PETSC_USE_LOG)
9755   PetscStageLog      stageLog;
9756   PetscLogEvent      event;
9757   PetscLogStage      stage;
9758   PetscEventPerfInfo eventInfo;
9759   PetscReal          cellRate, flopRate;
9760   PetscInt           cStart, cEnd, Nf, N;
9761   const char        *name;
9762 #endif
9763 
9764   PetscFunctionBegin;
9765   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9766 #if defined(PETSC_USE_LOG)
9767   PetscCall(PetscObjectGetName((PetscObject)dm, &name));
9768   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9769   PetscCall(DMGetNumFields(dm, &Nf));
9770   PetscCall(PetscLogGetStageLog(&stageLog));
9771   PetscCall(PetscStageLogGetCurrent(stageLog, &stage));
9772   PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
9773   PetscCall(PetscLogEventGetPerfInfo(stage, event, &eventInfo));
9774   N        = (cEnd - cStart) * Nf * eventInfo.count;
9775   flopRate = eventInfo.flops / eventInfo.time;
9776   cellRate = N / eventInfo.time;
9777   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)));
9778 #else
9779   SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off. Reconfigure using --with-log.");
9780 #endif
9781   PetscFunctionReturn(0);
9782 }
9783