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